feat(dashboard,types,js-sdk,payment): ability to refund payment in order page (#8385)
* feat(dashboard,types,js-sdk,payment): ability to refund payment in order page * chore: use confirmation variant for capture payment * chore: change refund design accords to figma * chore: move to js-sdk + currency input
This commit is contained in:
1 parent
6efdba1967
commit
7ae1d80380
14 files changed
+308
-15
No files matched your search
@@ -8,9 +8,13 @@ import {
|
|||||||
} from "@tanstack/react-query"
|
} from "@tanstack/react-query"
|
||||||
import { client, sdk } from "../../lib/client"
|
import { client, sdk } from "../../lib/client"
|
||||||
import { queryClient } from "../../lib/query-client"
|
import { queryClient } from "../../lib/query-client"
|
||||||
|
import { queryKeysFactory } from "../../lib/query-key-factory"
|
||||||
import { PaymentProvidersListRes } from "../../types/api-responses"
|
import { PaymentProvidersListRes } from "../../types/api-responses"
|
||||||
import { ordersQueryKeys } from "./orders"
|
import { ordersQueryKeys } from "./orders"
|
||||||
|
|
||||||
|
const PAYMENT_QUERY_KEY = "payment" as const
|
||||||
|
export const paymentQueryKeys = queryKeysFactory(PAYMENT_QUERY_KEY)
|
||||||
|
|
||||||
export const usePaymentProviders = (
|
export const usePaymentProviders = (
|
||||||
query?: Record<string, any>,
|
query?: Record<string, any>,
|
||||||
options?: Omit<
|
options?: Omit<
|
||||||
@@ -32,6 +36,28 @@ export const usePaymentProviders = (
|
|||||||
return { ...data, ...rest }
|
return { ...data, ...rest }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const usePayment = (
|
||||||
|
id: string,
|
||||||
|
query?: HttpTypes.AdminPaymentFilters,
|
||||||
|
options?: Omit<
|
||||||
|
UseQueryOptions<
|
||||||
|
HttpTypes.AdminPaymentResponse,
|
||||||
|
Error,
|
||||||
|
HttpTypes.AdminPaymentResponse,
|
||||||
|
QueryKey
|
||||||
|
>,
|
||||||
|
"queryKey" | "queryFn"
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { data, ...rest } = useQuery({
|
||||||
|
queryFn: () => sdk.admin.payment.retrieve(id, query),
|
||||||
|
queryKey: paymentQueryKeys.detail(id),
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
|
||||||
|
return { ...data, ...rest }
|
||||||
|
}
|
||||||
|
|
||||||
export const useCapturePayment = (
|
export const useCapturePayment = (
|
||||||
paymentId: string,
|
paymentId: string,
|
||||||
options?: UseMutationOptions<
|
options?: UseMutationOptions<
|
||||||
@@ -56,3 +82,28 @@ export const useCapturePayment = (
|
|||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useRefundPayment = (
|
||||||
|
paymentId: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
HttpTypes.AdminPaymentResponse,
|
||||||
|
Error,
|
||||||
|
HttpTypes.AdminRefundPayment
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (payload) => sdk.admin.payment.refund(paymentId, payload),
|
||||||
|
onSuccess: (data, variables, context) => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersQueryKeys.details(),
|
||||||
|
})
|
||||||
|
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersQueryKeys.lists(),
|
||||||
|
})
|
||||||
|
|
||||||
|
options?.onSuccess?.(data, variables, context)
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -826,7 +826,10 @@
|
|||||||
"requiresAction": "Requires action"
|
"requiresAction": "Requires action"
|
||||||
},
|
},
|
||||||
"capturePayment": "Payment of {{amount}} will be captured.",
|
"capturePayment": "Payment of {{amount}} will be captured.",
|
||||||
"capturePaymentSuccess": "Payment of {{amount}} successfully captured"
|
"capturePaymentSuccess": "Payment of {{amount}} successfully captured",
|
||||||
|
"createRefund": "Create Refund",
|
||||||
|
"refundPaymentSuccess": "Refund of amount {{amount}} successful",
|
||||||
|
"createRefundWrongQuantity": "Quantity should be a number between 1 and {{number}}"
|
||||||
},
|
},
|
||||||
|
|
||||||
"edits": {
|
"edits": {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { getRequest } from "./common"
|
|
||||||
import { PaymentProvidersListRes } from "../../types/api-responses"
|
import { PaymentProvidersListRes } from "../../types/api-responses"
|
||||||
|
import { getRequest } from "./common"
|
||||||
|
|
||||||
async function listPaymentProviders(query?: Record<string, any>) {
|
async function listPaymentProviders(query?: Record<string, any>) {
|
||||||
return getRequest<PaymentProvidersListRes, Record<string, any>>(
|
return getRequest<PaymentProvidersListRes, Record<string, any>>(
|
||||||
|
|||||||
@@ -238,6 +238,11 @@ export const RouteMap: RouteObject[] = [
|
|||||||
lazy: () =>
|
lazy: () =>
|
||||||
import("../../routes/orders/order-create-return"),
|
import("../../routes/orders/order-create-return"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "payments/:paymentId/refund",
|
||||||
|
lazy: () =>
|
||||||
|
import("../../routes/orders/order-create-refund"),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
+137
@@ -0,0 +1,137 @@
|
|||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import { Button, CurrencyInput, toast } from "@medusajs/ui"
|
||||||
|
import { useForm } from "react-hook-form"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import * as zod from "zod"
|
||||||
|
import { Form } from "../../../../../components/common/form"
|
||||||
|
import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
|
||||||
|
import { useRefundPayment } from "../../../../../hooks/api"
|
||||||
|
import { getCurrencySymbol } from "../../../../../lib/data/currencies"
|
||||||
|
import { formatCurrency } from "../../../../../lib/format-currency"
|
||||||
|
|
||||||
|
type CreateRefundFormProps = {
|
||||||
|
payment: HttpTypes.AdminPayment
|
||||||
|
}
|
||||||
|
|
||||||
|
const CreateRefundSchema = zod.object({
|
||||||
|
amount: zod.number(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const CreateRefundForm = ({ payment }: CreateRefundFormProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { handleSuccess } = useRouteModal()
|
||||||
|
const paymentAmount = payment.amount as unknown as number
|
||||||
|
|
||||||
|
const form = useForm<zod.infer<typeof CreateRefundSchema>>({
|
||||||
|
defaultValues: {
|
||||||
|
amount: paymentAmount,
|
||||||
|
},
|
||||||
|
resolver: zodResolver(CreateRefundSchema),
|
||||||
|
})
|
||||||
|
|
||||||
|
const { mutateAsync, isPending } = useRefundPayment(payment.id)
|
||||||
|
|
||||||
|
const handleSubmit = form.handleSubmit(async (data) => {
|
||||||
|
await mutateAsync(
|
||||||
|
{
|
||||||
|
amount: data.amount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success(
|
||||||
|
t("orders.payment.refundPaymentSuccess", {
|
||||||
|
amount: formatCurrency(data.amount, payment.currency_code),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
handleSuccess()
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(error.message)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RouteDrawer.Form form={form}>
|
||||||
|
<form onSubmit={handleSubmit} className="flex flex-1 flex-col">
|
||||||
|
<RouteDrawer.Body>
|
||||||
|
<div className="flex flex-col gap-y-4">
|
||||||
|
<Form.Field
|
||||||
|
control={form.control}
|
||||||
|
name="amount"
|
||||||
|
rules={{
|
||||||
|
required: true,
|
||||||
|
min: 0,
|
||||||
|
max: paymentAmount,
|
||||||
|
}}
|
||||||
|
render={({ field: { onChange, ...field } }) => {
|
||||||
|
return (
|
||||||
|
<Form.Item>
|
||||||
|
<Form.Label>{t("fields.amount")}</Form.Label>
|
||||||
|
|
||||||
|
<Form.Control>
|
||||||
|
<CurrencyInput
|
||||||
|
{...field}
|
||||||
|
min={0}
|
||||||
|
onChange={(e) => {
|
||||||
|
const val =
|
||||||
|
e.target.value === ""
|
||||||
|
? null
|
||||||
|
: Number(e.target.value)
|
||||||
|
|
||||||
|
onChange(val)
|
||||||
|
|
||||||
|
if (val && !isNaN(val)) {
|
||||||
|
if (val < 0 || val > paymentAmount) {
|
||||||
|
form.setError(`amount`, {
|
||||||
|
type: "manual",
|
||||||
|
message: t(
|
||||||
|
"orders.payment.createRefundWrongQuantity",
|
||||||
|
{ number: paymentAmount }
|
||||||
|
),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
form.clearErrors(`amount`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
code={payment.currency_code}
|
||||||
|
symbol={getCurrencySymbol(payment.currency_code)}
|
||||||
|
value={field.value}
|
||||||
|
/>
|
||||||
|
</Form.Control>
|
||||||
|
|
||||||
|
<Form.ErrorMessage />
|
||||||
|
</Form.Item>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</RouteDrawer.Body>
|
||||||
|
|
||||||
|
<RouteDrawer.Footer>
|
||||||
|
<div className="flex items-center justify-end gap-x-2">
|
||||||
|
<RouteDrawer.Close asChild>
|
||||||
|
<Button variant="secondary" size="small">
|
||||||
|
{t("actions.cancel")}
|
||||||
|
</Button>
|
||||||
|
</RouteDrawer.Close>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
isLoading={isPending}
|
||||||
|
type="submit"
|
||||||
|
variant="primary"
|
||||||
|
size="small"
|
||||||
|
disabled={!!Object.keys(form.formState.errors || {}).length}
|
||||||
|
>
|
||||||
|
{t("actions.save")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</RouteDrawer.Footer>
|
||||||
|
</form>
|
||||||
|
</RouteDrawer.Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export * from "./create-refund-form"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { OrderCreateRefund as Component } from "./order-create-refund"
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
import { Heading } from "@medusajs/ui"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import { useParams } from "react-router-dom"
|
||||||
|
import { RouteDrawer } from "../../../components/modals"
|
||||||
|
import { usePayment } from "../../../hooks/api"
|
||||||
|
import { CreateRefundForm } from "./components/create-refund-form"
|
||||||
|
|
||||||
|
export const OrderCreateRefund = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const params = useParams()
|
||||||
|
const { payment, isLoading, isError, error } = usePayment(params.paymentId!)
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RouteDrawer>
|
||||||
|
<RouteDrawer.Header>
|
||||||
|
<Heading>{t("orders.payment.createRefund")}</Heading>
|
||||||
|
</RouteDrawer.Header>
|
||||||
|
|
||||||
|
{!isLoading && payment && <CreateRefundForm payment={payment} />}
|
||||||
|
</RouteDrawer>
|
||||||
|
)
|
||||||
|
}
|
||||||
+21
-12
@@ -49,6 +49,7 @@ export const OrderPaymentSection = ({ order }: OrderPaymentSectionProps) => {
|
|||||||
<Header />
|
<Header />
|
||||||
|
|
||||||
<PaymentBreakdown
|
<PaymentBreakdown
|
||||||
|
order={order}
|
||||||
payments={payments}
|
payments={payments}
|
||||||
refunds={refunds}
|
refunds={refunds}
|
||||||
currencyCode={order.currency_code}
|
currencyCode={order.currency_code}
|
||||||
@@ -77,7 +78,6 @@ const Refund = ({
|
|||||||
currencyCode: string
|
currencyCode: string
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const hasPayment = refund.payment_id !== null
|
|
||||||
|
|
||||||
const BadgeComponent = (
|
const BadgeComponent = (
|
||||||
<Badge size="2xsmall" className="cursor-default select-none capitalize">
|
<Badge size="2xsmall" className="cursor-default select-none capitalize">
|
||||||
@@ -93,17 +93,20 @@ const Refund = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-ui-bg-subtle text-ui-fg-subtle grid grid-cols-[1fr_1fr_1fr_1fr_20px] items-center gap-x-4 px-6 py-4">
|
<div className="bg-ui-bg-subtle text-ui-fg-subtle grid grid-cols-[1fr_1fr_1fr_1fr_20px] items-center gap-x-4 px-6 py-4">
|
||||||
<div>
|
<div className="flex flex-row">
|
||||||
{hasPayment && <ArrowDownRightMini className="text-ui-fg-muted" />}
|
<div className="self-center pr-3">
|
||||||
<Text size="small" leading="compact" weight="plus">
|
<ArrowDownRightMini className="text-ui-fg-muted" />
|
||||||
{t("orders.payment.refund")}
|
</div>
|
||||||
</Text>
|
<div>
|
||||||
</div>
|
<Text size="small" leading="compact" weight="plus">
|
||||||
<div className="flex items-center justify-end">
|
{t("orders.payment.refund")}
|
||||||
<Text size="small" leading="compact">
|
</Text>
|
||||||
{format(new Date(refund.created_at), "dd MMM, yyyy, HH:mm:ss")}
|
<Text size="small" leading="compact">
|
||||||
</Text>
|
{format(new Date(refund.created_at), "dd MMM, yyyy, HH:mm:ss")}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-center justify-end"></div>
|
||||||
<div className="flex items-center justify-end">{Render}</div>
|
<div className="flex items-center justify-end">{Render}</div>
|
||||||
<div className="flex items-center justify-end">
|
<div className="flex items-center justify-end">
|
||||||
<Text size="small" leading="compact">
|
<Text size="small" leading="compact">
|
||||||
@@ -115,10 +118,12 @@ const Refund = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Payment = ({
|
const Payment = ({
|
||||||
|
order,
|
||||||
payment,
|
payment,
|
||||||
refunds,
|
refunds,
|
||||||
currencyCode,
|
currencyCode,
|
||||||
}: {
|
}: {
|
||||||
|
order: HttpTypes.AdminOrder
|
||||||
payment: MedusaPayment
|
payment: MedusaPayment
|
||||||
refunds: MedusaRefund[]
|
refunds: MedusaRefund[]
|
||||||
currencyCode: string
|
currencyCode: string
|
||||||
@@ -135,6 +140,7 @@ const Payment = ({
|
|||||||
}),
|
}),
|
||||||
confirmText: t("actions.confirm"),
|
confirmText: t("actions.confirm"),
|
||||||
cancelText: t("actions.cancel"),
|
cancelText: t("actions.cancel"),
|
||||||
|
variant: "confirmation",
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
@@ -204,7 +210,7 @@ const Payment = ({
|
|||||||
{
|
{
|
||||||
label: t("orders.payment.refund"),
|
label: t("orders.payment.refund"),
|
||||||
icon: <XCircle />,
|
icon: <XCircle />,
|
||||||
to: `/orders/${payment.order_id}/refund?paymentId=${payment.id}`,
|
to: `/orders/${order.id}/payments/${payment.id}/refund`,
|
||||||
disabled: !payment.captured_at,
|
disabled: !payment.captured_at,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -236,10 +242,12 @@ const Payment = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const PaymentBreakdown = ({
|
const PaymentBreakdown = ({
|
||||||
|
order,
|
||||||
payments,
|
payments,
|
||||||
refunds,
|
refunds,
|
||||||
currencyCode,
|
currencyCode,
|
||||||
}: {
|
}: {
|
||||||
|
order: HttpTypes.AdminOrder
|
||||||
payments: MedusaPayment[]
|
payments: MedusaPayment[]
|
||||||
refunds: MedusaRefund[]
|
refunds: MedusaRefund[]
|
||||||
currencyCode: string
|
currencyCode: string
|
||||||
@@ -271,6 +279,7 @@ const PaymentBreakdown = ({
|
|||||||
return (
|
return (
|
||||||
<Payment
|
<Payment
|
||||||
key={event.id}
|
key={event.id}
|
||||||
|
order={order}
|
||||||
payment={event}
|
payment={event}
|
||||||
refunds={refunds.filter(
|
refunds={refunds.filter(
|
||||||
(refund) => refund.payment_id === event.id
|
(refund) => refund.payment_id === event.id
|
||||||
|
|||||||
@@ -8,13 +8,37 @@ export class Payment {
|
|||||||
this.client = client
|
this.client = client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async list(query?: HttpTypes.AdminPaymentFilters, headers?: ClientHeaders) {
|
||||||
|
return await this.client.fetch<HttpTypes.AdminPaymentsResponse>(
|
||||||
|
`/admin/payments`,
|
||||||
|
{
|
||||||
|
query,
|
||||||
|
headers,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async retrieve(
|
||||||
|
id: string,
|
||||||
|
query?: HttpTypes.AdminPaymentFilters,
|
||||||
|
headers?: ClientHeaders
|
||||||
|
) {
|
||||||
|
return await this.client.fetch<HttpTypes.AdminPaymentResponse>(
|
||||||
|
`/admin/payments/${id}`,
|
||||||
|
{
|
||||||
|
query,
|
||||||
|
headers,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async capture(
|
async capture(
|
||||||
id: string,
|
id: string,
|
||||||
body: HttpTypes.AdminCapturePayment,
|
body: HttpTypes.AdminCapturePayment,
|
||||||
query?: SelectParams,
|
query?: SelectParams,
|
||||||
headers?: ClientHeaders
|
headers?: ClientHeaders
|
||||||
) {
|
) {
|
||||||
return await this.client.fetch<{ payment: HttpTypes.AdminPayment }>(
|
return await this.client.fetch<HttpTypes.AdminPaymentResponse>(
|
||||||
`/admin/payments/${id}/capture`,
|
`/admin/payments/${id}/capture`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -24,4 +48,21 @@ export class Payment {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async refund(
|
||||||
|
id: string,
|
||||||
|
body: HttpTypes.AdminRefundPayment,
|
||||||
|
query?: SelectParams,
|
||||||
|
headers?: ClientHeaders
|
||||||
|
) {
|
||||||
|
return await this.client.fetch<HttpTypes.AdminPaymentResponse>(
|
||||||
|
`/admin/payments/${id}/refund`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
BasePayment,
|
BasePayment,
|
||||||
BasePaymentCollection,
|
BasePaymentCollection,
|
||||||
BasePaymentCollectionFilters,
|
BasePaymentCollectionFilters,
|
||||||
|
BasePaymentFilters,
|
||||||
BasePaymentProvider,
|
BasePaymentProvider,
|
||||||
BasePaymentProviderFilters,
|
BasePaymentProviderFilters,
|
||||||
BasePaymentSession,
|
BasePaymentSession,
|
||||||
@@ -28,6 +29,16 @@ export interface AdminCapturePayment {
|
|||||||
amount?: number
|
amount?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AdminRefundPayment {
|
||||||
|
amount?: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface AdminPaymentResponse {
|
export interface AdminPaymentResponse {
|
||||||
payment: AdminPayment
|
payment: AdminPayment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AdminPaymentsResponse {
|
||||||
|
payments: AdminPayment[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdminPaymentFilters extends BasePaymentFilters {}
|
||||||
@@ -420,3 +420,7 @@ export interface BasePaymentProviderFilters
|
|||||||
id?: string | string[]
|
id?: string | string[]
|
||||||
region_id?: string | string[]
|
region_id?: string | string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BasePaymentFilters extends BaseFilterable<BasePaymentFilters> {
|
||||||
|
id?: string | string[]
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ export const defaultAdminPaymentFields = [
|
|||||||
"captures.amount",
|
"captures.amount",
|
||||||
"refunds.id",
|
"refunds.id",
|
||||||
"refunds.amount",
|
"refunds.amount",
|
||||||
|
"refunds.payment_id",
|
||||||
]
|
]
|
||||||
|
|
||||||
export const listTransformQueryConfig = {
|
export const listTransformQueryConfig = {
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ export default class Refund {
|
|||||||
})
|
})
|
||||||
payment!: Rel<Payment>
|
payment!: Rel<Payment>
|
||||||
|
|
||||||
|
@Property({ columnType: "text", nullable: true })
|
||||||
|
payment_id: string
|
||||||
|
|
||||||
@Property({
|
@Property({
|
||||||
onCreate: () => new Date(),
|
onCreate: () => new Date(),
|
||||||
columnType: "timestamptz",
|
columnType: "timestamptz",
|
||||||
|
|||||||
Reference in new issue
Block a user