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 activity_params
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
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
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)
@folder.destroy

head :no_content
end

# PATCH /price_lists/:price_list_id/product_price_folders/reorder
def reorder
authorize ProductPriceFolder, :reorder?

folder_positions = params.require(:folder_positions)

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
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
authorize @product_price, :update?

folder_id = params[:folder_id]

# 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
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
end
end

# PATCH /price_lists/:price_list_id/product_prices/reorder
def reorder
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
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