Fix(admin-ui, medusa): stock location fixes (#3395)

**What**
- A series of minor fixes for admin-ui relating to managing stock locations: 
  - make "create location" `primary`
  - add delete prompt when cancelling creation if information has been input
  - avoid clipping focus border on country select when creating a stock location
  - allow removals of sales channels from stock locations 

Fixes CORE-1191, CORE-1192, CORE-1190, CORE-1189
This commit is contained in:
Philip Korsholm
2023-03-07 11:54:09 +01:00
committed by GitHub
parent aadc1e19e9
commit 15f47baf56
5 changed files with 66 additions and 26 deletions

View File

@@ -0,0 +1,6 @@
---
"@medusajs/admin-ui": patch
"@medusajs/medusa": patch
---
fix(admin-ui, medusa): Minor ui fixes relating to stock locations

View File

@@ -1,8 +1,9 @@
import React, { useState } from "react"
import useNotification from "../../hooks/use-notification"
import { getErrorMessage } from "../../utils/error-messages"
import Button from "../fundamentals/button"
import Modal from "../molecules/modal"
import { getErrorMessage } from "../../utils/error-messages"
import useNotification from "../../hooks/use-notification"
type DeletePromptProps = {
heading?: string
@@ -31,7 +32,11 @@ const DeletePrompt: React.FC<DeletePromptProps> = ({
setIsLoading(true)
onDelete()
.then(() => notification("Success", successText, "success"))
.then(() => {
if (successText) {
notification("Success", successText, "success")
}
})
.catch((err) => notification("Error", getErrorMessage(err), "error"))
.finally(() => {
setIsLoading(false)
@@ -45,14 +50,14 @@ const DeletePrompt: React.FC<DeletePromptProps> = ({
<Modal.Content>
<div className="flex flex-col">
<span className="inter-large-semibold">{heading}</span>
<span className="inter-base-regular mt-1 text-grey-50">{text}</span>
<span className="inter-base-regular text-grey-50 mt-1">{text}</span>
</div>
</Modal.Content>
<Modal.Footer>
<div className="flex w-full h-8 justify-end">
<div className="flex h-8 w-full justify-end">
<Button
variant="ghost"
className="mr-2 w-24 text-small justify-center"
className="text-small min-w-24 mr-2 justify-center"
size="small"
onClick={handleClose}
>
@@ -61,7 +66,7 @@ const DeletePrompt: React.FC<DeletePromptProps> = ({
<Button
loading={isLoading}
size="small"
className="w-24 text-small justify-center"
className="text-small w-24 justify-center"
variant="nuclear"
onClick={handleSubmit}
disabled={isLoading}

View File

@@ -1,11 +1,12 @@
import { StockLocationExpandedDTO } from "@medusajs/medusa"
import { SalesChannel, StockLocationExpandedDTO } from "@medusajs/medusa"
import {
useAdminAddLocationToSalesChannel,
useAdminRemoveLocationFromSalesChannel,
} from "medusa-react"
import Button from "../../../../../components/fundamentals/button"
import useToggleState from "../../../../../hooks/use-toggle-state"
import SalesChannelsModal from "../../../../products/components/sales-channels-modal"
import useToggleState from "../../../../../hooks/use-toggle-state"
const EditSalesChannels = ({
location,
@@ -23,7 +24,7 @@ const EditSalesChannels = ({
const { mutateAsync: removeLocationFromSalesChannel } =
useAdminRemoveLocationFromSalesChannel()
const onSave = async (channels) => {
const onSave = async (channels: SalesChannel[]) => {
const existingChannels = location.sales_channels
const channelsToRemove =
existingChannels?.filter(

View File

@@ -4,22 +4,25 @@ import {
StockLocationAddressDTO,
StockLocationAddressInput,
} from "@medusajs/medusa"
import GeneralForm, { GeneralFormType } from "../components/general-form"
import {
useAdminAddLocationToSalesChannel,
useAdminCreateStockLocation,
} from "medusa-react"
import { useForm } from "react-hook-form"
import Accordion from "../../../../components/organisms/accordion"
import AddressForm from "../components/address-form"
import Button from "../../../../components/fundamentals/button"
import CrossIcon from "../../../../components/fundamentals/icons/cross-icon"
import DeletePrompt from "../../../../components/organisms/delete-prompt"
import FocusModal from "../../../../components/molecules/modal/focus-modal"
import Accordion from "../../../../components/organisms/accordion"
import useNotification from "../../../../hooks/use-notification"
import { useFeatureFlag } from "../../../../providers/feature-flag-provider"
import SalesChannelsForm from "../components/sales-channels-form"
import { getErrorMessage } from "../../../../utils/error-messages"
import { nestedForm } from "../../../../utils/nested-form"
import AddressForm from "../components/address-form"
import GeneralForm, { GeneralFormType } from "../components/general-form"
import SalesChannelsForm from "../components/sales-channels-form"
import { useFeatureFlag } from "../../../../providers/feature-flag-provider"
import { useForm } from "react-hook-form"
import useNotification from "../../../../hooks/use-notification"
import useToggleState from "../../../../hooks/use-toggle-state"
type NewLocationForm = {
general: GeneralFormType
@@ -43,7 +46,16 @@ const NewLocation = ({ onClose }: { onClose: () => void }) => {
reValidateMode: "onBlur",
mode: "onBlur",
})
const { handleSubmit, formState } = form
const {
handleSubmit,
formState: { isDirty, isValid },
} = form
const {
state: isShowingClosePrompt,
open: showClosePrompt,
close: closeClosePrompt,
} = useToggleState()
const notification = useNotification()
const { isFeatureEnabled } = useFeatureFlag()
@@ -58,6 +70,14 @@ const NewLocation = ({ onClose }: { onClose: () => void }) => {
location_id: locationId,
})
const handleClose = () => {
if (!isDirty) {
onClose()
} else {
showClosePrompt()
}
}
const onSubmit = () =>
handleSubmit(async (data) => {
const { locationPayload, salesChannelsPayload } = createPayload(data)
@@ -89,8 +109,6 @@ const NewLocation = ({ onClose }: { onClose: () => void }) => {
}
})
const { isDirty, isValid } = formState
return (
<form className="w-full">
<FocusModal>
@@ -100,14 +118,24 @@ const NewLocation = ({ onClose }: { onClose: () => void }) => {
size="small"
variant="ghost"
type="button"
onClick={onClose}
onClick={handleClose}
>
<CrossIcon size={20} />
</Button>
{isShowingClosePrompt && (
<DeletePrompt
heading="Are you sure you want to cancel with unsaved changes"
confirmText="Yes, cancel"
cancelText="No, continue creating"
successText={undefined}
handleClose={closeClosePrompt}
onDelete={async () => onClose()}
/>
)}
<div className="gap-x-small flex">
<Button
size="small"
variant="secondary"
variant="primary"
type="button"
disabled={!isDirty || !isValid}
onClick={onSubmit()}
@@ -131,7 +159,7 @@ const NewLocation = ({ onClose }: { onClose: () => void }) => {
<p className="inter-base-regular text-grey-50">
Specify the details about this location
</p>
<div className="mt-xlarge gap-y-xlarge flex flex-col">
<div className="mt-xlarge gap-y-xlarge flex flex-col pb-0.5">
<GeneralForm form={nestedForm(form, "general")} />
<AddressForm form={nestedForm(form, "address")} />
</div>

View File

@@ -1,7 +1,7 @@
import { IsString } from "class-validator"
import { Request, Response } from "express"
import { EntityManager } from "typeorm"
import { EntityManager } from "typeorm"
import { IsString } from "class-validator"
import { SalesChannelLocationService } from "../../../../services"
/**
@@ -80,7 +80,7 @@ export default async (req: Request, res: Response) => {
await manager.transaction(async (transactionManager) => {
await channelLocationService
.withTransaction(transactionManager)
.removeLocation(id, validatedBody.location_id)
.removeLocation(validatedBody.location_id, id)
})
res.json({