Skip to content

Commit ecb54f7

Browse files
committed
feat: create controllers with authentication and authorization
- Application controller with authentication and role-based access - Home controller for landing page and navigation - Users controller for profile management - Admin controllers for dashboard and user management - Admin imports controller for CSV functionality - Helper modules for view assistance and utilities
1 parent aa6ea34 commit ecb54f7

File tree

13 files changed

+265
-0
lines changed

13 files changed

+265
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class Admin::DashboardController < ApplicationController
2+
before_action :ensure_admin!
3+
4+
def index
5+
result = DashboardStatsService.call(current_user)
6+
7+
if result.success?
8+
stats = result.data
9+
@user_stats = stats[:users]
10+
@import_stats = stats[:imports]
11+
@activity_stats = stats[:activity]
12+
@growth_stats = stats[:growth]
13+
@recent_users = stats[:users][:recent]
14+
else
15+
redirect_to root_path, alert: result.error_messages.join(", ")
16+
end
17+
end
18+
end
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
class Admin::ImportsController < ApplicationController
2+
before_action :ensure_admin!
3+
before_action :set_import, only: [ :show ]
4+
5+
def index
6+
@imports = Import.includes(:user)
7+
.recent
8+
.page(params[:page])
9+
.per(10)
10+
@pending_imports = Import.where(status: "pending").count
11+
@processing_imports = Import.where(status: "processing").count
12+
end
13+
14+
def show
15+
respond_to do |format|
16+
format.html
17+
format.json { render json: import_json }
18+
end
19+
end
20+
21+
def create
22+
@import = current_user.imports.build
23+
24+
if params[:file].present?
25+
@import.file.attach(params[:file])
26+
@import.file_name = params[:file].original_filename
27+
28+
if @import.save
29+
# Enqueue background job to process the import
30+
UserImportJob.perform_later(@import)
31+
32+
redirect_to admin_import_path(@import),
33+
notice: "Import started successfully. Processing will begin shortly."
34+
else
35+
redirect_to admin_imports_path,
36+
alert: "Import failed: #{@import.errors.full_messages.join(', ')}"
37+
end
38+
else
39+
redirect_to admin_imports_path, alert: "Please select a file to import."
40+
end
41+
end
42+
43+
private
44+
45+
def set_import
46+
@import = Import.find(params[:id])
47+
end
48+
49+
def import_json
50+
{
51+
id: @import.id,
52+
status: @import.status,
53+
progress: @import.progress,
54+
total_rows: @import.total_rows,
55+
processed_rows: @import.processed_rows,
56+
successful_rows: @import.successful_rows,
57+
failed_rows: @import.failed_rows,
58+
success_rate: @import.success_rate,
59+
estimated_time_remaining: @import.estimated_time_remaining,
60+
created_at: @import.created_at,
61+
updated_at: @import.updated_at
62+
}
63+
end
64+
end
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
class Admin::UsersController < ApplicationController
2+
before_action :ensure_admin!
3+
before_action :set_user, only: [ :show, :edit, :update, :destroy, :toggle_role ]
4+
5+
def index
6+
@users = User.with_attached_avatar_image.order(:created_at)
7+
@users = @users.where(role: params[:role]) if params[:role].present?
8+
@users = @users.where("full_name ILIKE ? OR email ILIKE ?", "%#{params[:search]}%", "%#{params[:search]}%") if params[:search].present?
9+
@users = @users.page(params[:page]).per(10)
10+
end
11+
12+
def show
13+
end
14+
15+
def new
16+
@user = User.new
17+
end
18+
19+
def create
20+
@user = User.new(user_params)
21+
@user.password = SecureRandom.hex(8) if @user.password.blank?
22+
23+
if @user.save
24+
UserMailer.welcome_email(@user, @user.password).deliver_later if Rails.env.production?
25+
redirect_to admin_user_path(@user), notice: "User was successfully created."
26+
else
27+
render :new, status: :unprocessable_entity
28+
end
29+
end
30+
31+
def edit
32+
end
33+
34+
def update
35+
user_update_params = user_params
36+
user_update_params.delete(:password) if user_update_params[:password].blank?
37+
38+
if @user.update(user_update_params)
39+
redirect_to admin_user_path(@user), notice: "User was successfully updated."
40+
else
41+
render :edit, status: :unprocessable_entity
42+
end
43+
end
44+
45+
def destroy
46+
if @user == current_user
47+
redirect_to admin_users_path, alert: "You cannot delete your own account."
48+
return
49+
end
50+
51+
@user.destroy
52+
redirect_to admin_users_path, notice: "User was successfully deleted."
53+
end
54+
55+
def toggle_role
56+
new_role = @user.admin? ? "user" : "admin"
57+
58+
if @user == current_user && new_role == "user"
59+
redirect_to admin_users_path, alert: "You cannot remove admin access from your own account."
60+
return
61+
end
62+
63+
@user.update(role: new_role)
64+
redirect_to admin_users_path, notice: "User role updated to #{new_role.humanize}."
65+
end
66+
67+
private
68+
69+
def set_user
70+
@user = User.find(params[:id])
71+
end
72+
73+
def user_params
74+
params.require(:user).permit(:full_name, :email, :password, :password_confirmation, :role, :avatar_url, :avatar_image)
75+
end
76+
end
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
class ApplicationController < ActionController::Base
2+
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
3+
allow_browser versions: :modern
4+
5+
# Devise configuration
6+
before_action :authenticate_user!
7+
before_action :configure_permitted_parameters, if: :devise_controller?
8+
9+
# Redirect after sign in
10+
def after_sign_in_path_for(resource)
11+
if resource.admin?
12+
admin_dashboard_path
13+
else
14+
profile_path
15+
end
16+
end
17+
18+
# Redirect after sign out
19+
def after_sign_out_path_for(resource_or_scope)
20+
new_user_session_path
21+
end
22+
23+
protected
24+
25+
def configure_permitted_parameters
26+
devise_parameter_sanitizer.permit(:sign_up, keys: [ :full_name, :avatar_url ])
27+
devise_parameter_sanitizer.permit(:account_update, keys: [ :full_name, :avatar_url, :avatar_image ])
28+
end
29+
30+
# Authorization helpers
31+
def ensure_admin!
32+
redirect_to root_path, alert: "Access denied." unless current_user&.admin?
33+
end
34+
35+
def ensure_user_or_admin!(user)
36+
redirect_to root_path, alert: "Access denied." unless current_user == user || current_user&.admin?
37+
end
38+
end

app/controllers/concerns/.keep

Whitespace-only changes.

app/controllers/home_controller.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class HomeController < ApplicationController
2+
skip_before_action :authenticate_user!, only: [ :index ]
3+
4+
def index
5+
if user_signed_in?
6+
if current_user.admin?
7+
redirect_to admin_dashboard_path
8+
else
9+
redirect_to profile_path
10+
end
11+
end
12+
end
13+
end
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
class UsersController < ApplicationController
2+
before_action :set_user
3+
before_action :ensure_user_or_admin!
4+
5+
def show
6+
end
7+
8+
def edit
9+
end
10+
11+
def update
12+
user_update_params = user_params
13+
user_update_params.delete(:password) if user_update_params[:password].blank?
14+
15+
if @user.update(user_update_params)
16+
redirect_to profile_path, notice: "Profile was successfully updated."
17+
else
18+
render :edit, status: :unprocessable_entity
19+
end
20+
end
21+
22+
def destroy
23+
if @user == current_user
24+
@user.destroy
25+
redirect_to root_path, notice: "Your account has been successfully deleted."
26+
else
27+
redirect_to profile_path, alert: "You can only delete your own account."
28+
end
29+
end
30+
31+
private
32+
33+
def set_user
34+
@user = current_user
35+
end
36+
37+
def ensure_user_or_admin!
38+
redirect_to root_path, alert: "Access denied." unless current_user == @user || current_user&.admin?
39+
end
40+
41+
def user_params
42+
params.require(:user).permit(:full_name, :email, :password, :password_confirmation, :avatar_url, :avatar_image)
43+
end
44+
end
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module Admin::DashboardHelper
2+
end
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module Admin::ImportsHelper
2+
end

app/helpers/admin/users_helper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module Admin::UsersHelper
2+
end

0 commit comments

Comments
 (0)