Skip to content

Commit

Permalink
feat(product,dashboard): Allow re-ordering images (#10187)
Browse files Browse the repository at this point in the history
* migration

* fix snapshot

* primarykey

* init work on dnd

* progress

* dnd

* undo changes

* undo changes

* undo changes

* undo changes

* fix firefox issue

* lint

* lint

* lint

* add changeset

* undo changes to product module

* set activator node

* init work on service layer

* alternative

* switch to OneToMany

* add tests

* progress

* update migration

* update approach and remove all references to images in product.ts tests

* handle delete images on empty array

* fix config and order type

* update changeset

* rm flag

* export type and fix type in test

* fix type

---------

Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
  • Loading branch information
kasperkristensen and olivermrbl authored Nov 25, 2024
1 parent b12408d commit 1659c9b
Show file tree
Hide file tree
Showing 32 changed files with 1,224 additions and 584 deletions.
7 changes: 7 additions & 0 deletions .changeset/flat-mugs-try.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@medusajs/dashboard": patch
"@medusajs/product": patch
"@medusajs/types": patch
---

feat(dashboard): Allow re-ordering product images
36 changes: 36 additions & 0 deletions integration-tests/http/__tests__/product/admin/product.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ medusaIntegrationTestRunner({
// BREAKING: Type input changed from {type: {value: string}} to {type_id: string}
type_id: baseType.id,
tags: [{ id: baseTag1.id }, { id: baseTag2.id }],
images: [{
url: "image-one",
},
{
url: "image-two",
},
],
}),
adminHeaders
)
Expand Down Expand Up @@ -116,6 +123,24 @@ medusaIntegrationTestRunner({

describe("/admin/products", () => {
describe("GET /admin/products", () => {
it("returns a list of products with images ordered by rank", async () => {
const res = await api.get("/admin/products", adminHeaders)

expect(res.status).toEqual(200)
expect(res.data.products).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: baseProduct.id,
images: expect.arrayContaining([
expect.objectContaining({ url: "image-one", rank: 0 }),
expect.objectContaining({ url: "image-two", rank: 1 }),
]),
}),
])
)
})


it("returns a list of products with all statuses when no status or invalid status is provided", async () => {
const res = await api
.get("/admin/products", adminHeaders)
Expand Down Expand Up @@ -965,6 +990,17 @@ medusaIntegrationTestRunner({
expect(hasPrices).toBe(true)
})

it("should get a product with images ordered by rank", async () => {
const res = await api.get(`/admin/products/${baseProduct.id}`, adminHeaders)

expect(res.data.product.images).toEqual(
expect.arrayContaining([
expect.objectContaining({ url: "image-one", rank: 0 }),
expect.objectContaining({ url: "image-two", rank: 1 }),
])
)
})

it("should get a product with prices", async () => {
const res = await api
.get(
Expand Down
43 changes: 43 additions & 0 deletions integration-tests/http/__tests__/product/store/product.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,14 @@ medusaIntegrationTestRunner({
prices: [{ amount: 3000, currency_code: "usd" }],
},
],
images: [
{
url: "image-one",
},
{
url: "image-two",
},
],
})
;[product2, [variant2]] = await createProducts({
title: "test product 2 uniquely",
Expand Down Expand Up @@ -620,6 +628,22 @@ medusaIntegrationTestRunner({
])
})

it("should list all products with images ordered by rank", async () => {
const response = await api.get("/store/products", storeHeaders)

expect(response.data.products).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: product.id,
images: expect.arrayContaining([
expect.objectContaining({ url: "image-one", rank: 0 }),
expect.objectContaining({ url: "image-two", rank: 1 }),
]),
}),
])
)
})

it("should list all products excluding variants", async () => {
let response = await api.get(
`/admin/products?fields=-variants`,
Expand Down Expand Up @@ -1406,6 +1430,14 @@ medusaIntegrationTestRunner({
],
},
],
images: [
{
url: "image-one",
},
{
url: "image-two",
}
],
})

const defaultSalesChannel = await createSalesChannel(
Expand Down Expand Up @@ -1454,6 +1486,17 @@ medusaIntegrationTestRunner({
)
})

it("should retrieve product with images ordered by rank", async () => {
const response = await api.get(`/store/products/${product.id}`, storeHeaders)

expect(response.data.product.images).toEqual(
expect.arrayContaining([
expect.objectContaining({ url: "image-one", rank: 0 }),
expect.objectContaining({ url: "image-two", rank: 1 }),
])
)
})

// TODO: There are 2 problems that need to be solved to enable this test
// 1. When adding product to another category, the product is being removed from earlier assigned categories
// 2. MikroORM seems to be doing a join strategy to load relationships, we need to send a separate query to fetch relationships
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import { Form } from "../../common/form"

type RouteModalFormProps<TFieldValues extends FieldValues> = PropsWithChildren<{
form: UseFormReturn<TFieldValues>
blockSearch?: boolean
blockSearchParams?: boolean
onClose?: (isSubmitSuccessful: boolean) => void
}>

export const RouteModalForm = <TFieldValues extends FieldValues = any>({
form,
blockSearch = false,
blockSearchParams: blockSearch = false,
children,
onClose,
}: RouteModalFormProps<TFieldValues>) => {
Expand Down
14 changes: 13 additions & 1 deletion packages/admin/dashboard/src/i18n/translations/$schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1578,6 +1578,12 @@
"create": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"header": {
"type": "string"
},
Expand Down Expand Up @@ -1742,6 +1748,8 @@
}
},
"required": [
"title",
"description",
"header",
"tabs",
"errors",
Expand Down Expand Up @@ -1990,6 +1998,9 @@
"action"
],
"additionalProperties": false
},
"successToast": {
"type": "string"
}
},
"required": [
Expand All @@ -2008,7 +2019,8 @@
"galleryLabel",
"downloadImageLabel",
"deleteImageLabel",
"emptyState"
"emptyState",
"successToast"
],
"additionalProperties": false
},
Expand Down
5 changes: 4 additions & 1 deletion packages/admin/dashboard/src/i18n/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,8 @@
"successToast": "Produkz {{title}} angepasst."
},
"create": {
"title": "Produkt erstellen",
"description": "Erstellen Sie ein neues Produkt.",
"header": "Allgemein",
"tabs": {
"details": "Details",
Expand Down Expand Up @@ -490,7 +492,8 @@
"header": "Noch keine Medien",
"description": "Fügen Sie dem Produkt Medien hinzu, um es in Ihrem Schaufenster zu präsentieren.",
"action": "Medien hinzufügen"
}
},
"successToast": "Medien wurden erfolgreich aktualisiert."
},
"discountableHint": "Wenn diese Option deaktiviert ist, werden auf dieses Produkt keine Rabatte gewährt.",
"noSalesChannels": "In keinem Vertriebskanal verfügbar",
Expand Down
5 changes: 4 additions & 1 deletion packages/admin/dashboard/src/i18n/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,8 @@
"successToast": "Product {{title}} was successfully updated."
},
"create": {
"title": "Create Product",
"description": "Create a new product.",
"header": "General",
"tabs": {
"details": "Details",
Expand Down Expand Up @@ -490,7 +492,8 @@
"header": "No media yet",
"description": "Add media to the product to showcase it in your storefront.",
"action": "Add media"
}
},
"successToast": "Media was successfully updated."
},
"discountableHint": "When unchecked, discounts will not be applied to this product.",
"noSalesChannels": "Not available in any sales channels",
Expand Down
7 changes: 5 additions & 2 deletions packages/admin/dashboard/src/i18n/translations/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,8 @@
"successToast": "Produkt {{title}} został pomyślnie zaktualizowany."
},
"create": {
"title": "Utwórz produkt",
"description": "Utwórz nowy produkt.",
"header": "Ogólne",
"tabs": {
"details": "Szczegóły",
Expand Down Expand Up @@ -490,7 +492,8 @@
"header": "Brak mediów",
"description": "Dodaj media do produktu, aby zaprezentować go w swoim sklepie.",
"action": "Dodaj media"
}
},
"successToast": "Media zostały pomyślnie zaktualizowane."
},
"discountableHint": "Jeśli odznaczone, rabaty nie będą stosowane do tego produktu.",
"noSalesChannels": "Niedostępny w żadnych kanałach sprzedaży",
Expand Down Expand Up @@ -2752,4 +2755,4 @@
"seconds_one": "Drugi",
"seconds_other": "Towary drugiej jakości"
}
}
}
5 changes: 4 additions & 1 deletion packages/admin/dashboard/src/i18n/translations/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,8 @@
"successToast": "Ürün {{title}} başarıyla güncellendi."
},
"create": {
"title": "Ürün Oluştur",
"description": "Yeni bir ürün oluşturun.",
"header": "Genel",
"tabs": {
"details": "Detaylar",
Expand Down Expand Up @@ -490,7 +492,8 @@
"header": "Henüz medya yok",
"description": "Ürünü mağazanızda sergilemek için medya ekleyin.",
"action": "Medya ekle"
}
},
"successToast": "Medya başarıyla güncellendi."
},
"discountableHint": "İşaretlenmediğinde, bu ürüne indirim uygulanmayacaktır.",
"noSalesChannels": "Hiçbir satış kanalında mevcut değil",
Expand Down

This file was deleted.

Loading

0 comments on commit 1659c9b

Please sign in to comment.