Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions app/assets/stylesheets/order_screen.scss
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@
background-color: $gray-200;
border-right: 2px solid $gray-400;
box-shadow: 2px 0 4px -2px $transparent-200;

&.edit-mode-disabled {
opacity: 0.5;
pointer-events: none;
user-select: none;
}
}

.user-details {
Expand Down Expand Up @@ -279,4 +285,170 @@
grid-area: order-grid;
overflow-y: auto;
}

// Folder styles
.folder-container {
display: contents;
}

.folder-tile {
position: relative;
background-color: $gray-600;
color: $white;

.folder-icon {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 0.5rem;
font-size: 3.5rem;
position: relative;
}

.folder-back-arrow {
position: absolute;
font-size: 1rem;
bottom: 0;
right: -0.5rem;
color: $white;
background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
padding: 0.2rem;
}

.product-grid-product-name {
color: $font-color-dark;
font-size: $font-size-lg;
text-shadow: none;
}

&.edit-mode {
cursor: grab;
}
}

.folder-edit-btn {
position: absolute;
top: 8px;
right: 8px;
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(255, 255, 255, 0.9);
border-radius: 50%;
cursor: pointer;
font-size: 0.8rem;
color: $gray-700;
transition: all 0.2s ease;

&:hover {
background-color: $white;
transform: scale(1.1);
}
}

.add-folder-tile {
background-color: $gray-400;
border: 2px dashed $gray-600;

.folder-icon {
color: $gray-600;
}

.product-grid-product-name {
color: $gray-600;
}

&:hover {
background-color: $gray-300;
}
}

.drop-home-tile {
background-color: $gray-500;
border: 2px dashed $gray-700;
}

.back-button-tile {
.folder-icon {
font-size: 2rem;
}
}

// Draggable styles
.draggable {
cursor: grab;

&:active {
cursor: grabbing;
}
}

.drag-handle {
position: absolute;
top: 8px;
left: 8px;
color: rgba(0, 0, 0, 0.3);
font-size: 1rem;
}

.sortable-ghost {
opacity: 0.4;
}

.sortable-chosen {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

.sortable-drag {
background-color: $white;
}

// Folder modal styles
.folder-modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1050;
}

.folder-modal {
background-color: $white;
border-radius: 8px;
width: 90%;
max-width: 400px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}

.folder-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem;
border-bottom: 1px solid $gray-300;

h5 {
margin: 0;
}
}

.folder-modal-body {
padding: 1rem;
}

.folder-modal-footer {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
padding: 1rem;
border-top: 1px solid $gray-300;
}
}
10 changes: 9 additions & 1 deletion app/controllers/activities_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,21 @@ def order_screen # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
.find(params[:id])

@product_prices_json = sorted_product_price(@activity).to_json(
only: %i[id price position product_price_folder_id],
include: { product: { only: %i[id name category color], methods: %i[requires_age] } }
)

@folders_json = @activity.price_list.product_price_folders.order(:position).to_json(
only: %i[id name position color]
)

@users_json = users_hash.to_json

@activity_json = @activity.to_json(only: %i[id title start_time end_time])

@is_treasurer = current_user.treasurer?
@price_list_id = @activity.price_list_id

@sumup_key = Rails.application.config.x.sumup_key
@sumup_enabled = @sumup_key.present?

Expand Down Expand Up @@ -174,7 +182,7 @@ def users_hash
end

def sorted_product_price(activity)
activity.price_list.product_price.sort_by { |p| p.product.id }
activity.price_list.product_price.includes(:product).order(:position)
end

def permitted_attributes
Expand Down
78 changes: 78 additions & 0 deletions app/controllers/product_price_folders_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
class ProductPriceFoldersController < ApplicationController
before_action :authenticate_user!
before_action :set_price_list, only: %i[index create reorder]
before_action :set_folder, only: %i[update destroy]

# GET /price_lists/:price_list_id/product_price_folders
def index
authorize ProductPriceFolder
@folders = @price_list.product_price_folders.order(:position)
render json: @folders
end

# POST /price_lists/:price_list_id/product_price_folders
def create
@folder = @price_list.product_price_folders.new(folder_params)
authorize @folder

if @folder.save
render json: @folder, status: :created
else
render json: { errors: @folder.errors.full_messages }, status: :unprocessable_entity

Check warning on line 21 in app/controllers/product_price_folders_controller.rb

View workflow job for this annotation

GitHub Actions / Lint

[Correctable] Rails/HttpStatusNameConsistency: Prefer :unprocessable_content over :unprocessable_entity.
end
end

# PATCH /product_price_folders/:id
def update
authorize @folder

if @folder.update(folder_params)
render json: @folder
else
render json: { errors: @folder.errors.full_messages }, status: :unprocessable_entity

Check warning on line 32 in app/controllers/product_price_folders_controller.rb

View workflow job for this annotation

GitHub Actions / Lint

[Correctable] Rails/HttpStatusNameConsistency: Prefer :unprocessable_content over :unprocessable_entity.
end
end

# DELETE /product_price_folders/:id
def destroy
authorize @folder

# Move all products in this folder back to home screen (nullify folder_id)
@folder.product_prices.update_all(product_price_folder_id: nil)

Check failure on line 41 in app/controllers/product_price_folders_controller.rb

View workflow job for this annotation

GitHub Actions / Lint

Rails/SkipsModelValidations: Avoid using update_all because it skips validations. (https://guides.rubyonrails.org/active_record_validations.html#skipping-validations)
@folder.destroy

head :no_content
end

# PATCH /price_lists/:price_list_id/product_price_folders/reorder
def reorder

Check failure on line 48 in app/controllers/product_price_folders_controller.rb

View workflow job for this annotation

GitHub Actions / Lint

Metrics/MethodLength: Method has too many lines. [11/10] (https://rubystyle.guide#short-methods)
authorize ProductPriceFolder, :reorder?

folder_positions = params.require(:folder_positions)

Check failure on line 52 in app/controllers/product_price_folders_controller.rb

View workflow job for this annotation

GitHub Actions / Lint

[Correctable] Layout/TrailingWhitespace: Trailing whitespace detected. (https://rubystyle.guide#no-trailing-whitespace)
ActiveRecord::Base.transaction do
folder_positions.each do |folder_data|
folder = @price_list.product_price_folders.find(folder_data[:id])
folder.update!(position: folder_data[:position])
end
end

render json: { success: true }
rescue ActiveRecord::RecordInvalid => e
render json: { errors: [e.message] }, status: :unprocessable_entity

Check warning on line 62 in app/controllers/product_price_folders_controller.rb

View workflow job for this annotation

GitHub Actions / Lint

[Correctable] Rails/HttpStatusNameConsistency: Prefer :unprocessable_content over :unprocessable_entity.
end

private

def set_price_list
@price_list = PriceList.find(params[:price_list_id])
end

def set_folder
@folder = ProductPriceFolder.find(params[:id])
end

def folder_params
params.require(:product_price_folder).permit(:name, :color, :position)
end
end
61 changes: 61 additions & 0 deletions app/controllers/product_prices_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
class ProductPricesController < ApplicationController
before_action :authenticate_user!
before_action :set_product_price, only: %i[assign_folder]
before_action :set_price_list, only: %i[reorder]

# PATCH /product_prices/:id/assign_folder
def assign_folder

Check failure on line 7 in app/controllers/product_prices_controller.rb

View workflow job for this annotation

GitHub Actions / Lint

Metrics/MethodLength: Method has too many lines. [13/10] (https://rubystyle.guide#short-methods)
authorize @product_price, :update?

folder_id = params[:folder_id]

Check failure on line 11 in app/controllers/product_prices_controller.rb

View workflow job for this annotation

GitHub Actions / Lint

[Correctable] Layout/TrailingWhitespace: Trailing whitespace detected. (https://rubystyle.guide#no-trailing-whitespace)
# Validate folder belongs to same price list if provided
if folder_id.present?
folder = ProductPriceFolder.find(folder_id)
unless folder.price_list_id == @product_price.price_list_id
return render json: { errors: ['Folder does not belong to the same price list'] }, status: :unprocessable_entity

Check warning on line 16 in app/controllers/product_prices_controller.rb

View workflow job for this annotation

GitHub Actions / Lint

[Correctable] Rails/HttpStatusNameConsistency: Prefer :unprocessable_content over :unprocessable_entity.
end
end

if @product_price.update(product_price_folder_id: folder_id)
render json: @product_price, include: product_price_includes
else
render json: { errors: @product_price.errors.full_messages }, status: :unprocessable_entity

Check warning on line 23 in app/controllers/product_prices_controller.rb

View workflow job for this annotation

GitHub Actions / Lint

[Correctable] Rails/HttpStatusNameConsistency: Prefer :unprocessable_content over :unprocessable_entity.
end
end

# PATCH /price_lists/:price_list_id/product_prices/reorder
def reorder

Check failure on line 28 in app/controllers/product_prices_controller.rb

View workflow job for this annotation

GitHub Actions / Lint

Metrics/MethodLength: Method has too many lines. [14/10] (https://rubystyle.guide#short-methods)
authorize ProductPrice, :update?

product_positions = params.require(:product_positions)

ActiveRecord::Base.transaction do
product_positions.each do |product_data|
product_price = @price_list.product_price.find(product_data[:id])
product_price.update!(
position: product_data[:position],
product_price_folder_id: product_data[:folder_id]
)
end
end

render json: { success: true }
rescue ActiveRecord::RecordInvalid => e
render json: { errors: [e.message] }, status: :unprocessable_entity

Check warning on line 45 in app/controllers/product_prices_controller.rb

View workflow job for this annotation

GitHub Actions / Lint

[Correctable] Rails/HttpStatusNameConsistency: Prefer :unprocessable_content over :unprocessable_entity.
end

private

def set_product_price
@product_price = ProductPrice.find(params[:id])
end

def set_price_list
@price_list = PriceList.find(params[:price_list_id])
end

def product_price_includes
{ product: { only: %i[id name category color], methods: %i[requires_age] } }
end
end
Loading
Loading