Skip to content

Commit

Permalink
Merge pull request #104 from TreinaDev/cadastro-visitantes-morador
Browse files Browse the repository at this point in the history
Morador cadastra um visitante/funcionário
  • Loading branch information
RyanOxon authored Jul 18, 2024
2 parents 1119da2 + a84ae9f commit 0f089ce
Show file tree
Hide file tree
Showing 32 changed files with 983 additions and 38 deletions.
1 change: 1 addition & 0 deletions app/controllers/condos_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def show
@towers = @condo.towers.order :name
@common_areas = @condo.common_areas.order :name
@unit_types = @condo.unit_types.order :description
@todays_visitors = (resident_signed_in? ? current_resident.todays_visitors : [])
end

def new
Expand Down
56 changes: 56 additions & 0 deletions app/controllers/visitors_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
class VisitorsController < ApplicationController
before_action :set_resident, only: %i[index new create]
before_action :authenticate_resident!, only: %i[index new create]
before_action :set_breadcrumbs_for_action, only: %i[new create]

def index
@visitors = @resident.visitors
end

def new
@visitor = Visitor.new
end

def create
@visitor = Visitor.new(visitor_params)
unless @visitor.save
flash.now[:alert] = t('alerts.visitor.not_created')
return render :new, status: :unprocessable_entity
end
set_visit_date_job
redirect_to resident_visitors_path(@resident), notice: I18n.t('notice.visitor.created')
end

private

def set_visit_date_job
return unless @visitor.employee?

UpdateVisitDateJob.set(wait_until: (@visitor.visit_date + 1.day).to_datetime).perform_later(@visitor)
end

def set_breadcrumbs_for_action
add_breadcrumb @resident.residence.condo.name, @resident.residence.condo
add_breadcrumb I18n.t("breadcrumb.visitor.#{action_name}")
end

def authenticate_resident!
return redirect_to root_path, alert: I18n.t('alerts.visitor.manager_block') if manager_signed_in?

if resident_signed_in?
return redirect_to root_path, alert: I18n.t('alerts.visitor.not_tenant') if @resident.residence.nil?
return redirect_to root_path, alert: I18n.t('alerts.visitor.not_allowed') unless current_resident == @resident
end

super
end

def set_resident
@resident = Resident.find(params[:resident_id])
end

def visitor_params
params.require(:visitor).permit(:full_name, :identity_number, :visit_date, :category,
:recurrence).merge resident: @resident
end
end
15 changes: 15 additions & 0 deletions app/javascript/controllers/hiddenfield_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="hiddenfield"
export default class extends Controller {
static targets = ["role", "field"];

connect() {
this.toggleField();
}

toggleField() {
const role = this.roleTarget.value;
role === "visitor" ? this.fieldTarget.classList.add("d-none") : this.fieldTarget.classList.remove("d-none");
}
}
2 changes: 2 additions & 0 deletions app/javascript/controllers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { application } from "./application"
import HelloController from "./hello_controller"
application.register("hello", HelloController)

import HiddenfieldController from "./hiddenfield_controller"
application.register("hiddenfield", HiddenfieldController)
import CondoController from "./condo_controller"
application.register("condo", CondoController)

Expand Down
14 changes: 14 additions & 0 deletions app/jobs/update_visit_date_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class UpdateVisitDateJob < ApplicationJob
queue_as :default

def perform(visitor)
return unless visitor.employee?

new_visit_date = visitor.next_recurrent_date

return if new_visit_date.nil?

visitor.update(visit_date: new_visit_date)
UpdateVisitDateJob.set(wait_until: (new_visit_date + 1.day).to_datetime).perform_later(visitor)
end
end
9 changes: 7 additions & 2 deletions app/models/resident.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class Resident < ApplicationRecord
has_one :residence, class_name: 'Unit', foreign_key: 'tenant_id', dependent: :nullify, inverse_of: :tenant
has_many :properties, class_name: 'Unit', foreign_key: 'owner_id', dependent: :nullify, inverse_of: :owner
has_one :residence, class_name: 'Unit', foreign_key: 'tenant_id', dependent: :destroy, inverse_of: :tenant
has_many :properties, class_name: 'Unit', foreign_key: 'owner_id', dependent: :destroy, inverse_of: :owner
has_many :visitors, dependent: :destroy

devise :database_authenticatable, :recoverable, :rememberable, :validatable
validate :valid_registration_number
Expand All @@ -11,6 +12,10 @@ class Resident < ApplicationRecord

enum status: { not_owner: 0, not_tenant: 1, mail_not_confirmed: 2, mail_confirmed: 3 }

def todays_visitors
visitors.where(visit_date: Date.current)
end

def description
"#{full_name} - #{email}"
end
Expand Down
55 changes: 55 additions & 0 deletions app/models/visitor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
class Visitor < ApplicationRecord
ID_REGEX = /\A[a-zA-Z0-9]+\z/

belongs_to :resident
enum category: { visitor: 0, employee: 1 }

enum recurrence: { once: 0, daily: 1, working_days: 2, weekly: 3, biweekly: 4,
monthly: 5, bimonthly: 6, quarterly: 7, semiannual: 8, annual: 9 }

validates :visit_date, :full_name, :identity_number, :category, presence: true
validate :date_is_future, on: :create
validates :recurrence, presence: true, if: -> { employee? }
validates :recurrence, absence: true, if: -> { visitor? }
validates :identity_number, length: { in: 5..10 }
validates :identity_number,
format: { with: ID_REGEX, message: I18n.t('alerts.visitor.only_numbers_and_letters') }

def next_recurrent_date
return if once? || recurrence.nil?

set_recurrence_date
end

private

def date_is_future
return unless visit_date.present? && visit_date < Time.zone.today

errors.add(:visit_date, 'deve ser futura.')
end

def set_recurrence_date
return days_recurrence if %w[daily working_days].include? recurrence
return weeks_recurrence if %w[weekly biweekly].include? recurrence
return months_recurrence if %w[monthly bimonthly quarterly semiannual].include? recurrence

visit_date + 1.year if annual?
end

def days_recurrence
return visit_date + 1.day if daily?

visit_date.next_weekday if working_days?
end

def weeks_recurrence
weeks_quantity = { 'weekly' => 1, 'biweekly' => 2 }
visit_date + weeks_quantity[recurrence].week
end

def months_recurrence
months_quantity = { 'monthly' => 1, 'bimonthly' => 2, 'quarterly' => 3, 'semiannual' => 6 }
visit_date + months_quantity[recurrence].month
end
end
2 changes: 1 addition & 1 deletion app/views/condos/dashboard/_common_area_list.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="accordion-item rounded-5 shadow-sm">
<h2 class="accordion-header">
<button class="accordion-button rounded-pill py-3" style="background-color: #FDE879" type="button" data-bs-toggle="collapse" data-bs-target="#common-areas" aria-expanded="false" aria-controls="collapseOne">
<button class="accordion-button collapsed rounded-pill py-3" style="background-color: #FDE879" type="button" data-bs-toggle="collapse" data-bs-target="#common-areas" aria-expanded="false" aria-controls="collapseOne">
<strong class="position-absolute top-50 start-50 translate-middle text-black">Lista de Áreas Comuns</strong>
</button>
</h2>
Expand Down
30 changes: 30 additions & 0 deletions app/views/condos/dashboard/_todays_visitors.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<div class="card rounded-5 shadow-sm" id="todays-visitors">
<strong class="card-header text-center rounded-top-5" style="background-color: #FDE879;">Visitantes e funcionários esperados para hoje</strong>
<div class="card-body">
<div class="d-flex justify-content-between mx-1">
<% if resident_signed_in? && current_resident.residence.present? %>
<%= link_to new_resident_visitor_path(current_resident), class:"btn btn-dark rounded-pill d-flex mb-2 shadow-sm" do %>
<i class="bi bi-bookmark-plus me-2"></i> <p style="margin: 0; font-size: 14px;">Cadastrar Visitante/Funcionário</p>
<% end %>
<% end %>
<%= link_to (resident_signed_in? ? resident_visitors_path(current_resident) : root_path), class:"btn btn-dark rounded-pill d-flex mb-2 shadow-sm" do %>
<p style="margin: 0; font-size: 14px;">Ver todos</p> <i class="bi bi-search ms-1"></i>
<% end %>
</div>

<% if @todays_visitors.any? %>
<% @todays_visitors.each do |visitor| %>
<div class="row justify-content-between align-items-center my-1" id="visitor-<%= visitor.id %>">
<div class="col d-flex">
<i class="bi bi-person"></i>
<p class="m-0 ms-1"><%= visitor.full_name %></p>
</div>
<span class="col-3 badge bg-dark-subtle text-dark rounded-pill me-3"><%= I18n.t("activerecord.attributes.visitor.categories.#{ visitor.category }") %></span>
</div>
<hr class="m-0">
<% end %>
<% else %>
<div class="alert alert-warning text-center mb-2"><%= I18n.t('notice.visitor.todays_empty') %></div>
<% end %>
</div>
</div>
2 changes: 1 addition & 1 deletion app/views/condos/dashboard/_tower_list.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="accordion-item rounded-5 shadow-sm">
<h2 class="accordion-header">
<button class="accordion-button rounded-pill py-3" style="background-color: #FDE879" type="button" data-bs-toggle="collapse" data-bs-target="#towers" aria-expanded="false" aria-controls="collapseOne">
<button class="accordion-button collapsed rounded-pill py-3" style="background-color: #FDE879" type="button" data-bs-toggle="collapse" data-bs-target="#towers" aria-expanded="false" aria-controls="collapseOne">
<strong class="position-absolute top-50 start-50 translate-middle text-black">Lista de Torres</strong>
</button>
</h2>
Expand Down
4 changes: 2 additions & 2 deletions app/views/condos/dashboard/_unit_type_list.html.erb
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<div class="accordion-item rounded-5 my-2 shadow-sm">
<h2 class="accordion-header">
<button class="accordion-button rounded-pill py-3" style="background-color: #FDE879" type="button" data-bs-toggle="collapse" data-bs-target="#unit-types" aria-expanded="false" aria-controls="collapseOne">
<button class="accordion-button collapsed rounded-pill py-3" style="background-color: #FDE879" type="button" data-bs-toggle="collapse" data-bs-target="#unit-types" aria-expanded="false" aria-controls="collapseOne">
<strong class="position-absolute top-50 start-50 translate-middle text-black">Lista de Tipos de Unidade</strong>
</button>
</h2>

<div id="unit-types" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
<div class="accordion-body">
<div class="accordion-body">
<% if @unit_types.any? %>
<% @unit_types.each do |unit_type| %>
<div class="d-flex align-items-center my-1">
Expand Down
10 changes: 2 additions & 8 deletions app/views/condos/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,8 @@
<strong>Lista de Entradas</strong>
<i class="bi bi-search ms-1"></i>
<% end %>
<% end %>

<div class="card rounded-5 shadow-sm">
<strong class="card-header text-center rounded-top-5" style="background-color: #FDE879;">Visitantes e funcionários esperados para hoje</strong>
<div class="card-body">
<div class="alert alert-warning text-center">Em breve</div>
</div>
</div>
<% end %>
<%= render 'condos/dashboard/todays_visitors' %>
</div>

<div class="col">
Expand Down
32 changes: 19 additions & 13 deletions app/views/shared/_navbar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,23 @@
</nav>

<div class="modal fade" id="condoSelectPopupForTowers" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="condoSelectPopupLabel" aria-hidden="true" >
<div class="modal-dialog modal-dialog-scrollable">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="condoSelectPopupLabel">Selecione o Condomínio</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>

<div class="modal-body">
<div class='container text-center'>
<div class='container-fluid'>
<div class='row'>
<% if current_manager %>
<% condos = current_manager.is_super ? Condo.all : current_manager.condos %>
<% condos.each do |condo| %>
<div class='col'>
<%= button_to condo.name, new_condo_tower_path(condo), method: :get, class:'btn btn-dark', data: { turbo: false } %>
<div class='col-auto'>
<%= link_to new_condo_tower_path(condo), class:"btn btn-dark rounded-pill d-flex align-items-center mb-2 shadow-sm", data: { turbo: false } do %>
<i class="bi bi-bookmark-plus me-2 mb-1"></i> <p style="margin: 0; font-size: 14px;"><%= condo.name %></p>
<% end %>
</div>
<% end %>
<% end %>
Expand All @@ -129,21 +131,23 @@
</div>

<div class="modal fade" id="condoSelectPopupForUnitTypes" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="condoSelectPopupLabel" aria-hidden="true" >
<div class="modal-dialog modal-dialog-scrollable">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="condoSelectPopupLabel">Selecione o Condomínio</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>

<div class="modal-body">
<div class='container text-center'>
<div class='container-fluid'>
<div class='row'>
<% if current_manager %>
<% condos = current_manager.is_super ? Condo.all : current_manager.condos %>
<% condos.each do |condo| %>
<div class='col'>
<%= button_to condo.name, new_condo_unit_type_path(condo), method: :get, class:'btn btn-dark', data: { turbo: false } %>
<div class='col-auto'>
<%= link_to new_condo_unit_type_path(condo), class:"btn btn-dark rounded-pill d-flex mb-2 shadow-sm", data: { turbo: false } do %>
<i class="bi bi-bookmark-plus me-2"></i> <p style="margin: 0; font-size: 14px;"><%= condo.name %></p>
<% end %>
</div>
<% end %>
<% end %>
Expand All @@ -159,21 +163,23 @@
</div>

<div class="modal fade" id="condoSelectPopupForCommonAreas" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="condoSelectPopupLabel" aria-hidden="true" >
<div class="modal-dialog modal-dialog-scrollable">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="condoSelectPopupLabel">Selecione o Condomínio</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>

<div class="modal-body">
<div class='container text-center'>
<div class='row'>
<div class='container-fluid'>
<div class='row '>
<% if current_manager %>
<% condos = current_manager.is_super ? Condo.all : current_manager.condos %>
<% condos.each do |condo| %>
<div class='col'>
<%= button_to condo.name, new_condo_common_area_path(condo), method: :get, class:'btn btn-dark', data: { turbo: false } %>
<div class='col-auto'>
<%= link_to new_condo_common_area_path(condo), class:"btn btn-dark rounded-pill d-flex mb-2 shadow-sm", data: { turbo: false } do %>
<i class="bi bi-bookmark-plus me-2"></i> <p style="margin: 0; font-size: 14px;"><%= condo.name %></p>
<% end %>
</div>
<% end %>
<% end %>
Expand Down
32 changes: 32 additions & 0 deletions app/views/visitors/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<div class='bg-white rounded-5 py-4 px-5 shadow'>

<div class="d-flex mb-5 justify-content-between">
<h1>Meus visitantes/funcionários registrados</h1>
<%= link_to 'Registrar Nova Entrada de Visitante', new_resident_visitor_path(@resident), class:'btn btn-dark rounded-pill px-4 m-2' %>
</div>

<table class="table table-sm">
<thead>
<tr>
<th scope="col">Nome Completo</th>
<th scope="col">RG</th>
<th scope="col">Categoria:</th>
<th scope="col">Proxima Data Autorizada:</th>
</tr>
</thead>
<tbody>
<% @visitors.each do |visitor| %>
<tr id="visitor-<%= visitor.id %>">
<td><%= visitor.full_name %></td>
<td><%= visitor.identity_number %></td>
<td><%= I18n.t("activerecord.attributes.visitor.categories.#{visitor.category}") %></td>
<td class="d-flex align-items-center"><%= I18n.l(visitor.visit_date) %>
<% if visitor.employee? %>
<span class="badge text-bg-warning ms-1 rounded-pill"><%= I18n.t("activerecord.attributes.visitor.recurrences.#{visitor.recurrence}") %></span>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
Loading

0 comments on commit 0f089ce

Please sign in to comment.