diff --git a/app/controllers/api/product_categories_controller.rb b/app/controllers/api/product_categories_controller.rb index 0a60e477d..c126e9616 100644 --- a/app/controllers/api/product_categories_controller.rb +++ b/app/controllers/api/product_categories_controller.rb @@ -40,7 +40,7 @@ class API::ProductCategoriesController < API::ApiController if @product_category.insert_at(params[:position]) render :show else - render json: @product_category.errors.full_messages, status: :unprocessable_entity + render json: @product_category.errors, status: :unprocessable_entity end end diff --git a/app/frontend/src/javascript/components/store/categories/product-categories-item.tsx b/app/frontend/src/javascript/components/store/categories/product-categories-item.tsx index 76e603246..0a591f278 100644 --- a/app/frontend/src/javascript/components/store/categories/product-categories-item.tsx +++ b/app/frontend/src/javascript/components/store/categories/product-categories-item.tsx @@ -1,11 +1,9 @@ -// TODO: Remove next eslint-disable -/* eslint-disable @typescript-eslint/no-unused-vars */ import React from 'react'; import { ProductCategory } from '../../../models/product-category'; import { useSortable } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; import { ManageProductCategory } from './manage-product-category'; -import { ArrowElbowDownRight, ArrowElbowLeftUp, CaretDown, DotsSixVertical } from 'phosphor-react'; +import { ArrowElbowDownRight, ArrowLeft, CaretDown, DotsSixVertical } from 'phosphor-react'; interface ProductCategoriesItemProps { productCategories: Array, @@ -42,7 +40,7 @@ export const ProductCategoriesItem: React.FC = ({ pr {((isDragging && offset) || status === 'child') &&
{(offset === 'down') && } - {(offset === 'up') && } + {(offset === 'up') && }
}
diff --git a/app/frontend/src/javascript/components/store/categories/product-categories-tree.tsx b/app/frontend/src/javascript/components/store/categories/product-categories-tree.tsx index 5bd94fa7e..ba503d5dd 100644 --- a/app/frontend/src/javascript/components/store/categories/product-categories-tree.tsx +++ b/app/frontend/src/javascript/components/store/categories/product-categories-tree.tsx @@ -1,6 +1,4 @@ -// TODO: Remove next eslint-disable -/* eslint-disable @typescript-eslint/no-unused-vars */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useImmer } from 'use-immer'; import { ProductCategory } from '../../../models/product-category'; import { DndContext, KeyboardSensor, PointerSensor, useSensor, useSensors, closestCenter, DragMoveEvent } from '@dnd-kit/core'; @@ -10,7 +8,7 @@ import { ProductCategoriesItem } from './product-categories-item'; interface ProductCategoriesTreeProps { productCategories: Array, - onDnd: (list: Array) => void, + onDnd: (list: Array, activeCategory: ProductCategory, position: number) => void, onSuccess: (message: string) => void, onError: (message: string) => void, } @@ -67,6 +65,10 @@ export const ProductCategoriesTree: React.FC = ({ pr setActiveData(draft => { return { ...draft, offset: 'down' }; }); + } else if (Math.ceil(delta.x) < -32 && getStatus(over.id) === 'child') { + setActiveData(draft => { + return { ...draft, offset: 'up' }; + }); } else { setActiveData(draft => { return { ...draft, offset: null }; @@ -98,47 +100,60 @@ export const ProductCategoriesTree: React.FC = ({ pr let newOrder = [...categoriesList]; const currentIdsOrder = over?.data.current.sortable.items; let newIndex = over.data.current.sortable.index; + let droppedItem = getCategory(active.id); + const activeStatus = getStatus(active.id); + const overStatus = getStatus(over.id); + let newPosition = getCategory(over.id).position; - // [A] Single |> [B] Single - if (getStatus(active.id) === 'single' && getStatus(over.id) === 'single') { + // [A]:Single dropped over [B]:Single + if (activeStatus === 'single' && overStatus === 'single') { const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex); newOrder = newIdsOrder.map(sortedId => { let category = getCategory(sortedId); if (activeData.offset === 'down' && sortedId === active.id && activeData.index < newIndex && active.id !== over.id) { category = { ...category, parent_id: Number(over.id) }; + droppedItem = category; } else if (activeData.offset === 'down' && sortedId === active.id && (activeData.index > newIndex || active.id === over.id)) { category = { ...category, parent_id: getPreviousAdopter(over.id) }; + droppedItem = category; } return category; }); } - // [A] Child |> [B] Single - if ((getStatus(active.id) === 'child') && getStatus(over.id) === 'single') { + // [A]:Child dropped over [B]:Single + if ((activeStatus === 'child') && overStatus === 'single') { const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex); newOrder = newIdsOrder.map(sortedId => { let category = getCategory(sortedId); if (activeData.offset === 'down' && sortedId === active.id && activeData.index < newIndex) { category = { ...category, parent_id: Number(over.id) }; + droppedItem = category; + newPosition = 0; } else if (activeData.offset === 'down' && sortedId === active.id && activeData.index > newIndex) { category = { ...category, parent_id: getPreviousAdopter(over.id) }; + droppedItem = category; + newPosition = getChildren(getPreviousAdopter(over.id))?.length || 0; } else if (sortedId === active.id) { category = { ...category, parent_id: null }; + droppedItem = category; } return category; }); } - // [A] Single || Child |>… - if (getStatus(active.id) === 'single' || getStatus(active.id) === 'child') { - // [B] Parent - if (getStatus(over.id) === 'parent') { + // [A]:Single || [A]:Child dropped over… + if (activeStatus === 'single' || activeStatus === 'child') { + // [B]:Parent + if (overStatus === 'parent') { const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex); if (activeData.index < newIndex) { newOrder = newIdsOrder.map(sortedId => { let category = getCategory(sortedId); if (sortedId === active.id) { category = { ...category, parent_id: Number(over.id) }; + droppedItem = category; + newPosition = 0; } return category; }); @@ -147,38 +162,54 @@ export const ProductCategoriesTree: React.FC = ({ pr let category = getCategory(sortedId); if (sortedId === active.id && !activeData.offset) { category = { ...category, parent_id: null }; + droppedItem = category; } else if (sortedId === active.id && activeData.offset === 'down') { category = { ...category, parent_id: getPreviousAdopter(over.id) }; + droppedItem = category; + newPosition = getChildren(getPreviousAdopter(over.id))?.length || 0; } return category; }); } } - // [B] Child - if (getStatus(over.id) === 'child') { - const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex); - newOrder = newIdsOrder.map(sortedId => { - let category = getCategory(sortedId); - if (sortedId === active.id) { - category = { ...category, parent_id: getCategory(over.id).parent_id }; - } - return category; - }); + // [B]:Child + if (overStatus === 'child') { + if (activeData.offset === 'up') { + const lastChildIndex = newOrder.findIndex(c => c.id === getChildren(getCategory(over.id).parent_id).pop().id); + const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, lastChildIndex); + newOrder = newIdsOrder.map(sortedId => { + let category = getCategory(sortedId); + if (sortedId === active.id) { + category = { ...category, parent_id: null }; + droppedItem = category; + } + return category; + }); + } else { + const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex); + newOrder = newIdsOrder.map(sortedId => { + let category = getCategory(sortedId); + if (sortedId === active.id) { + category = { ...category, parent_id: getCategory(over.id).parent_id }; + droppedItem = category; + } + return category; + }); + } } } - // [A] Parent |>… - if (getStatus(active.id) === 'parent') { - // [B] Single - if (getStatus(over.id) === 'single') { + // [A]:Parent dropped over… + if (activeStatus === 'parent') { + // [B]:Single + if (overStatus === 'single') { const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex); newOrder = newIdsOrder.map(sortedId => getCategory(sortedId)); } - // [B] Parent - if (getStatus(over.id) === 'parent') { + // [B]:Parent + if (overStatus === 'parent') { if (activeData.index < newIndex) { - const lastOverChildIndex = newOrder.findIndex(c => c.id === getChildren(over.id).pop().id); - newIndex = lastOverChildIndex; + newIndex = newOrder.findIndex(c => c.id === getChildren(over.id).pop().id); const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex); newOrder = newIdsOrder.map(sortedId => getCategory(sortedId)); } else { @@ -186,17 +217,15 @@ export const ProductCategoriesTree: React.FC = ({ pr newOrder = newIdsOrder.map(sortedId => getCategory(sortedId)); } } - // [B] Child - if (getStatus(over.id) === 'child') { + // [B]:Child + if (overStatus === 'child') { if (activeData.index < newIndex) { const parent = newOrder.find(c => c.id === getCategory(over.id).parent_id); - const lastSiblingIndex = newOrder.findIndex(c => c.id === getChildren(parent.id).pop().id); - newIndex = lastSiblingIndex; + newIndex = newOrder.findIndex(c => c.id === getChildren(parent.id).pop().id); const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex); newOrder = newIdsOrder.map(sortedId => getCategory(sortedId)); } else { - const parentIndex = currentIdsOrder.indexOf(getCategory(over.id).parent_id); - newIndex = parentIndex; + newIndex = currentIdsOrder.indexOf(getCategory(over.id).parent_id); const newIdsOrder = arrayMove(currentIdsOrder, activeData.index, newIndex); newOrder = newIdsOrder.map(sortedId => getCategory(sortedId)); } @@ -204,8 +233,9 @@ export const ProductCategoriesTree: React.FC = ({ pr // insert children back newOrder = showChildren(active.id, newOrder, newIndex); } + setActiveData(initActiveData); - onDnd(newOrder); + onDnd(newOrder, droppedItem, newPosition); }; /** @@ -283,7 +313,7 @@ export const ProductCategoriesTree: React.FC = ({ pr }; /** - * Toggle parent category by hidding/showing its children + * Toggle parent category by hiding/showing its children */ const handleCollapse = (id) => { const i = collapsed.findIndex(el => el === id); diff --git a/app/frontend/src/javascript/components/store/categories/product-categories.tsx b/app/frontend/src/javascript/components/store/categories/product-categories.tsx index be45f7b8e..2fda32f85 100644 --- a/app/frontend/src/javascript/components/store/categories/product-categories.tsx +++ b/app/frontend/src/javascript/components/store/categories/product-categories.tsx @@ -46,8 +46,16 @@ const ProductCategories: React.FC = ({ onSuccess, onErro /** * Update state after drop */ - const handleDnd = (data: ProductCategory[]) => { - setProductCategories(data); + const handleDnd = (list: ProductCategory[], activeCategory: ProductCategory, position: number) => { + setProductCategories(list); + ProductCategoryAPI + .update(activeCategory) + .then(c => { + ProductCategoryAPI + .updatePosition(c, position) + .catch(error => onError(error)); + }) + .catch(error => onError(error)); }; /** @@ -60,10 +68,9 @@ const ProductCategories: React.FC = ({ onSuccess, onErro }; /** - * Save list's new order + * tmp: check list */ const handleSave = () => { - // TODO: index to position -> send to API console.log('save order:', productCategories); }; @@ -75,7 +82,7 @@ const ProductCategories: React.FC = ({ onSuccess, onErro - Save + [log]
diff --git a/app/frontend/src/javascript/components/store/store.tsx b/app/frontend/src/javascript/components/store/store.tsx index 221172cff..01c15afed 100644 --- a/app/frontend/src/javascript/components/store/store.tsx +++ b/app/frontend/src/javascript/components/store/store.tsx @@ -49,7 +49,7 @@ const Store: React.FC = ({ onError, onSuccess, currentUser }) => { const [currentPage, setCurrentPage] = useState(1); useEffect(() => { - ProductAPI.index({ page: 1, is_active: true }).then(data => { + ProductAPI.index({ page: 1, is_active: filterVisible }).then(data => { setPageCount(data.total_pages); setProducts(data.products); }).catch(() => { @@ -151,7 +151,7 @@ const Store: React.FC = ({ onError, onSuccess, currentUser }) => { */ const handlePagination = (page: number) => { if (page !== currentPage) { - ProductAPI.index({ page, is_active: true }).then(data => { + ProductAPI.index({ page, is_active: filterVisible }).then(data => { setCurrentPage(page); setProducts(data.products); setPageCount(data.total_pages);