Skip to content

Commit

Permalink
many many changes.
Browse files Browse the repository at this point in the history
* broke out authenticated and non-authenticated layouts
* added some tailwind stimulus components
* wrote a simple stimulus component to drive the mobile nav UI
* added a basic tailwind-based layout
* some model tweaks
  • Loading branch information
fermion committed Nov 28, 2023
1 parent 72ed34d commit 0c8ff89
Show file tree
Hide file tree
Showing 13 changed files with 257 additions and 19 deletions.
6 changes: 6 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@ class ApplicationController < ActionController::Base

helper_method :current_user

layout :choose_layout

private

def choose_layout
current_user.blank? ? 'public' : 'application'
end

def current_user
@current_user ||= authenticate_by_session(User)
end
Expand Down
24 changes: 24 additions & 0 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,26 @@
module ApplicationHelper
def header_link_to(text, path)
css_classes = if current_page?(path)
"bg-gray-900 text-white rounded-md px-3 py-2 text-sm font-medium"
else
"text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium"
end

link_to text, path, class: css_classes
end

def mobile_header_link_to(text, path)
css_classes = if current_page?(path)
"bg-gray-900 text-white block px-3 py-2 rounded-md text-base font-medium"
else
"text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"
end

link_to text, path, class: css_classes
end
def user_gravatar(user:, css_classes: "h-8 w-8 rounded-full")
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
gravatar_url = "http://secure.gravatar.com/avatar/#{gravatar_id}"
image_tag(gravatar_url, alt: user.name, class: css_classes)
end
end
5 changes: 5 additions & 0 deletions app/javascript/controllers/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import { Application } from "@hotwired/stimulus"

const application = Application.start()

// Import and register all TailwindCSS Components or just the ones you need
import { Alert, Autosave, ColorPreview, Dropdown, Modal, Tabs, Popover, Toggle, Slideover } from "tailwindcss-stimulus-components"
application.register('alert', Alert)
application.register('dropdown', Dropdown)

// Configure Stimulus development experience
application.debug = false
window.Stimulus = application
Expand Down
16 changes: 8 additions & 8 deletions app/javascript/controllers/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Import and register all your controllers from the importmap under controllers/*
// This file is auto-generated by ./bin/rails stimulus:manifest:update
// Run that command whenever you add a new controller or create them with
// ./bin/rails generate stimulus controllerName

import { application } from "controllers/application"
import { application } from "./application"

// Eager load all controllers defined in the import map under controllers/**/*_controller
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
eagerLoadControllersFrom("controllers", application)
import MobileNavController from "./mobile_nav_controller"
application.register("mobile-nav", MobileNavController)

// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!)
// import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
// lazyLoadControllersFrom("controllers", application)
import WorkWeekController from "./work_week_controller"
application.register("work-week", WorkWeekController)
34 changes: 34 additions & 0 deletions app/javascript/controllers/mobile_nav_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="mobile-nav"
export default class extends Controller {
static targets = ['open', 'close']
static values = { open: Boolean, default: false }

openValueChanged() {
if (this.openValue === true) {
this.show()
} else {
this.hide()
}
}

show() {
this.closeTarget.classList.replace('hidden', 'block')
this.openTarget.classList.replace('block', 'hidden')
document.getElementById('mobile-menu').classList.replace('hidden', 'block')
this.openValue = true;
}

hide() {
this.closeTarget.classList.replace('block', 'hidden')
this.openTarget.classList.replace('hidden', 'block')
document.getElementById('mobile-menu').classList.replace('block', 'hidden')
this.openValue = false
}

toggle() {
this.openValue = !this.openValue
}
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="work-week"
export default class extends Controller {
connect() {
this.element.textContent = "Hello World!"
}
}
11 changes: 9 additions & 2 deletions app/models/membership.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@ class Membership < ApplicationRecord
belongs_to :user
belongs_to :company

OWNER = 'owner'.freeze
ADMIN = 'admin'.freeze
MEMBER = 'member'.freeze

VALID_ROLES = [OWNER, ADMIN, MEMBER].freeze
VALID_STATUSES = %w[active inactive].freeze

validates :user, presence: true, uniqueness: { scope: :company }
validates :company, presence: true, uniqueness: { scope: :user }
validates :status, presence: true, inclusion: { in: %w[active inactive] }
validates :role, presence: true, inclusion: { in: %w[owner admin member] }
validates :status, presence: true, inclusion: { in: VALID_STATUSES }
validates :role, presence: true, inclusion: { in: VALID_ROLES }
end
4 changes: 4 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ class User < ApplicationRecord
format: { with: URI::MailTo::EMAIL_REGEXP }

passwordless_with :email

def owner?
memberships.find_by(company: current_company).role == Membership::OWNER
end
end
146 changes: 140 additions & 6 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html class="h-full bg-gray-100">
<head>
<title>StaffplanRedux</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
Expand All @@ -11,9 +11,143 @@
<%= javascript_importmap_tags %>
</head>

<body>
<main class="container mx-auto mt-28 px-5 flex">
<%= yield %>
</main>
<body class="h-full">
<div class="min-h-full">
<div class="bg-gray-800 pb-32">
<nav class="bg-gray-800">
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
<div class="border-b border-gray-700">
<div class="flex h-16 items-center justify-between px-4 sm:px-0">
<div class="flex items-center">
<div class="flex-shrink-0">
<img class="h-8 w-8" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" alt="Your Company">
</div>
<div class="hidden md:block">
<div class="ml-10 flex items-baseline space-x-4">
<!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" -->
<%= header_link_to "My StaffPlan", staff_plan_path(current_user) %>
<a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium">Clients</a>
<a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium">Projects</a>
<a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium">People</a>
<% if current_user.owner? %>
<a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium">Employee Management</a>
<a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium">Settings</a>
<% end %>
</div>
</div>
</div>
<div class="hidden md:block">
<div class="ml-4 flex items-center md:ml-6">
<button type="button" class="relative rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800">
<span class="absolute -inset-1.5"></span>
<span class="sr-only">View notifications</span>
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" />
</svg>
</button>

<!-- Profile dropdown -->
<div data-controller="dropdown" data-action="click->dropdown#toggle click@window->dropdown#hide">
<div class="relative ml-3">
<div>
<button type="button"
data-dropdown-target="button"
class="relative flex max-w-xs items-center rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800"
aria-expanded="false"
aria-haspopup="true"
title="<%= current_user.email %>">
<span class="absolute -inset-1.5"></span>
<span class="sr-only">Open user menu</span>
<%= user_gravatar(user: current_user) %>
</button>
</div>
<div data-dropdown-target="menu"
data-transition-enter="transition ease-out duration-100"
data-transition-enter-from="transform opacity-0 scale-95"
data-transition-enter-to="transform opacity-100 scale-100"
data-transition-leave="transition ease-in duration-75"
data-transition-leave-from="transform opacity-100 scale-100"
data-transition-leave-to="transform opacity-0 scale-95"
class="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="user-menu-button" tabindex="-1">
<%= link_to "Sign out", auth_sign_out_path, class: "block px-4 py-2 text-sm text-gray-700", role: "menuitem" %>
</div>
</div>
</div>
</div>
</div>

<div class="-mr-2 flex md:hidden">
<!-- Mobile menu button -->
<div data-controller="mobile-nav" data-action="click->mobile-nav#toggle">
<button data-mobile-nav-target="button" type="button" class="relative inline-flex items-center justify-center rounded-md bg-gray-800 p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" aria-controls="mobile-menu" aria-expanded="false">
<span class="absolute -inset-0.5"></span>
<span class="sr-only">Open main menu</span>
<!-- Menu open: "hidden", Menu closed: "block" -->
<svg data-mobile-nav-target="open" class="block h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
</svg>
<!-- Menu open: "block", Menu closed: "hidden" -->
<svg data-mobile-nav-target="close" class="hidden h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
</div>
</div>
</div>

<!-- Mobile menu, show/hide based on menu state. -->
<div class="border-b border-gray-700 md:hidden hidden" id="mobile-menu">
<div class="space-y-1 px-2 py-3 sm:px-3">
<%= mobile_header_link_to "My StaffPlan", staff_plan_path(current_user) %>
<a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Clients</a>
<a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Projects</a>
<a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">People</a>
<% if current_user.owner? %>
<a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Employee Management</a>
<a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Settings</a>
<% end %>
</div>
<div class="border-t border-gray-700 pb-3 pt-4">
<div class="flex items-center px-5">
<div class="flex-shrink-0">
<%= user_gravatar(user: current_user, css_classes: "h-10 w-10 rounded-full") %>
</div>
<div class="ml-3">
<div class="text-base font-medium leading-none text-white"><%= current_user.name %></div>
<div class="text-sm font-medium leading-none text-gray-400"><%= current_user.email %></div>
</div>
<button type="button" class="relative ml-auto flex-shrink-0 rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800">
<span class="absolute -inset-1.5"></span>
<span class="sr-only">View notifications</span>
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" />
</svg>
</button>
</div>
<div class="mt-3 space-y-1 px-2">
<%= link_to "Sign out", auth_sign_out_path, class: "block rounded-md px-3 py-2 text-base font-medium text-gray-400 hover:bg-gray-700 hover:text-white" %>
</div>
</div>
</div>
</nav>
<% if content_for(:header) %>
<header class="py-10">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<h1 class="text-xl font-bold tracking-tight text-white"><%= yield(:header) %></h1>
</div>
</header>
<% end %>
</div>

<main class="-mt-32">
<div class="mx-auto max-w-7xl px-4 pb-12 sm:px-6 lg:px-8">
<div class="rounded-lg bg-white px-5 py-6 shadow sm:px-6">
<%= yield %>
</div>
</div>
</main>
</div>
</body>
</html>
</html>
19 changes: 19 additions & 0 deletions app/views/layouts/public.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>StaffplanRedux</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
</head>

<body>
<main class="container mx-auto mt-28 px-5 flex">
<%= yield %>
</main>
</body>
</html>
3 changes: 3 additions & 0 deletions app/views/staff_plan/users/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<% content_for(:header) do %>
<%= current_user == @viewing_user ? "My StaffPlan" : "#{@viewing_user.name}'s StaffPlan" %>
<% end %>
3 changes: 2 additions & 1 deletion config/importmap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

pin "application", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus", to: "https://ga.jspm.io/npm:@hotwired/stimulus@3.2.2/dist/stimulus.js"
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
pin "tailwindcss-stimulus-components", to: "https://ga.jspm.io/npm:tailwindcss-stimulus-components@4.0.4/dist/tailwindcss-stimulus-components.module.js"
3 changes: 2 additions & 1 deletion config/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ module.exports = {
'./public/*.html',
'./app/helpers/**/*.rb',
'./app/javascript/**/*.js',
'./app/views/**/*.{erb,haml,html,slim}'
'./app/views/**/*.{erb,haml,html,slim}',
'./vendor/javascript/tailwindcss-stimulus-components.js'
],
theme: {
extend: {
Expand Down

0 comments on commit 0c8ff89

Please sign in to comment.