@mohasinac/appkit 3.5.6 → 3.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/tour/TourProvider.d.ts +11 -3
- package/dist/_internal/client/features/tour/TourProvider.js +93 -4
- package/dist/_internal/server/features/checkout/actions.js +65 -11
- package/dist/_internal/server/features/orders/adapters.js +3 -0
- package/dist/_internal/shared/actions/action-registry.js +20 -0
- package/dist/_internal/shared/checkout/order-math.d.ts +21 -0
- package/dist/_internal/shared/checkout/order-math.js +24 -0
- package/dist/_internal/shared/features/orders/config.d.ts +1 -1
- package/dist/_internal/shared/features/orders/config.js +1 -1
- package/dist/_internal/shared/fees/calculator.d.ts +13 -0
- package/dist/_internal/shared/fees/calculator.js +8 -0
- package/dist/client.d.ts +8 -0
- package/dist/client.js +8 -0
- package/dist/constants/api-endpoints.d.ts +9 -0
- package/dist/constants/api-endpoints.js +3 -0
- package/dist/features/admin/components/AdminBundleEditorView.d.ts +8 -1
- package/dist/features/admin/components/AdminBundleEditorView.js +15 -11
- package/dist/features/admin/components/AdminPayoutsView.js +41 -4
- package/dist/features/admin/components/AdminSiteSettingsView.js +49 -1
- package/dist/features/admin/schemas/firestore.d.ts +8 -0
- package/dist/features/admin/schemas/firestore.js +6 -0
- package/dist/features/layout/TitleBarLayout.js +4 -4
- package/dist/features/messages/hooks/useConversations.d.ts +7 -1
- package/dist/features/messages/hooks/useConversations.js +7 -6
- package/dist/features/orders/actions/order-actions.d.ts +10 -0
- package/dist/features/orders/actions/order-actions.js +57 -4
- package/dist/features/orders/schemas/firestore.d.ts +16 -0
- package/dist/features/orders/types/index.d.ts +2 -0
- package/dist/features/products/schemas/firestore.d.ts +4 -0
- package/dist/features/scams/actions/scam-actions.d.ts +12 -0
- package/dist/features/scams/actions/scam-actions.js +31 -0
- package/dist/features/scams/components/SellerTrustBadge.d.ts +16 -0
- package/dist/features/scams/components/SellerTrustBadge.js +21 -0
- package/dist/features/scams/components/index.d.ts +2 -0
- package/dist/features/scams/components/index.js +1 -0
- package/dist/features/seller/components/SellerBundlesView.d.ts +5 -5
- package/dist/features/seller/components/SellerBundlesView.js +81 -84
- package/dist/features/seller/components/SellerProductShell.d.ts +2 -0
- package/dist/features/seller/components/SellerProductShell.js +7 -1
- package/dist/features/stores/components/StoreDetailLayoutView.d.ts +8 -1
- package/dist/features/stores/components/StoreDetailLayoutView.js +9 -3
- package/dist/features/stores/components/StoreHeader.d.ts +4 -1
- package/dist/features/stores/components/StoreHeader.js +3 -2
- package/dist/index.d.ts +7 -3
- package/dist/index.js +6 -2
- package/dist/jobs.d.ts +2 -0
- package/dist/jobs.js +6 -0
- package/dist/next/routing/route-map.d.ts +3 -0
- package/dist/next/routing/route-map.js +1 -0
- package/dist/security/rate-limit.d.ts +5 -0
- package/dist/security/rate-limit.js +2 -0
- package/dist/seed/categories-seed-data.js +180 -1
- package/dist/seed/grouped-listings-seed-data.d.ts +2 -0
- package/dist/seed/products-preorders-seed-data.d.ts +2 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +5 -0
- package/package.json +2 -1
|
@@ -8,20 +8,73 @@ import { NotFoundError, AuthorizationError, ValidationError, } from "../../../er
|
|
|
8
8
|
import { serverLogger } from "../../../monitoring";
|
|
9
9
|
import { getProviders } from "../../../contracts";
|
|
10
10
|
import { orderRepository } from "../repository/orders.repository";
|
|
11
|
+
import { ORDER_CANCELLABLE_STATUSES } from "../../../_internal/shared/features/orders/config";
|
|
11
12
|
const ERR_ORDER_NOT_FOUND = "Order not found";
|
|
12
|
-
|
|
13
|
+
function assertCancellable(order) {
|
|
14
|
+
if (!ORDER_CANCELLABLE_STATUSES.includes(order.status)) {
|
|
15
|
+
throw new ValidationError("Only pending or confirmed orders can be cancelled");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
13
18
|
export async function cancelOrderForUser(userId, orderId, reason) {
|
|
14
19
|
const order = await orderRepository.findById(orderId);
|
|
15
20
|
if (!order)
|
|
16
21
|
throw new NotFoundError(ERR_ORDER_NOT_FOUND);
|
|
17
22
|
if (order.userId !== userId)
|
|
18
23
|
throw new AuthorizationError("You are not authorised to cancel this order");
|
|
19
|
-
|
|
20
|
-
throw new ValidationError("Only pending or confirmed orders can be cancelled");
|
|
21
|
-
}
|
|
24
|
+
assertCancellable(order);
|
|
22
25
|
await orderRepository.cancelOrder(orderId, reason);
|
|
23
26
|
serverLogger.info("Order cancelled by user", { userId, orderId, reason });
|
|
24
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Cancels a subset of line items on an already-placed order and lets the rest
|
|
30
|
+
* proceed. Matches today's whole-order cancellation behavior: marks the items
|
|
31
|
+
* cancelled + sets `refundPending: true`, leaves the actual refund to the
|
|
32
|
+
* existing admin-initiated flow (no auto-refund call here). If every item on
|
|
33
|
+
* the order ends up cancelled, falls through to the same whole-order
|
|
34
|
+
* `cancelOrder` path `cancelOrderForUser` uses, rather than leaving a
|
|
35
|
+
* zero-item live order.
|
|
36
|
+
*/
|
|
37
|
+
export async function cancelOrderItemsForUser(userId, orderId, itemIds, reason) {
|
|
38
|
+
if (itemIds.length === 0) {
|
|
39
|
+
return cancelOrderForUser(userId, orderId, reason);
|
|
40
|
+
}
|
|
41
|
+
const order = await orderRepository.findById(orderId);
|
|
42
|
+
if (!order)
|
|
43
|
+
throw new NotFoundError(ERR_ORDER_NOT_FOUND);
|
|
44
|
+
if (order.userId !== userId)
|
|
45
|
+
throw new AuthorizationError("You are not authorised to cancel this order");
|
|
46
|
+
assertCancellable(order);
|
|
47
|
+
const items = order.items ?? [];
|
|
48
|
+
const targetIds = new Set(itemIds);
|
|
49
|
+
const matchedIds = new Set(items.filter((i) => targetIds.has(i.productId)).map((i) => i.productId));
|
|
50
|
+
if (matchedIds.size === 0) {
|
|
51
|
+
throw new ValidationError("None of the selected items belong to this order");
|
|
52
|
+
}
|
|
53
|
+
const allCancelled = items.every((i) => matchedIds.has(i.productId) || i.cancelledQuantity != null);
|
|
54
|
+
if (allCancelled) {
|
|
55
|
+
return cancelOrderForUser(userId, orderId, reason);
|
|
56
|
+
}
|
|
57
|
+
const cancelledAt = new Date();
|
|
58
|
+
const updatedItems = items.map((item) => matchedIds.has(item.productId)
|
|
59
|
+
? {
|
|
60
|
+
...item,
|
|
61
|
+
cancelledQuantity: item.quantity,
|
|
62
|
+
cancelledAt,
|
|
63
|
+
cancelledReason: reason,
|
|
64
|
+
}
|
|
65
|
+
: item);
|
|
66
|
+
await orderRepository.update(orderId, {
|
|
67
|
+
items: updatedItems,
|
|
68
|
+
refundPending: true,
|
|
69
|
+
updatedAt: cancelledAt,
|
|
70
|
+
});
|
|
71
|
+
serverLogger.info("Order items cancelled by user", {
|
|
72
|
+
userId,
|
|
73
|
+
orderId,
|
|
74
|
+
itemIds: [...matchedIds],
|
|
75
|
+
reason,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
25
78
|
export async function listOrdersForUser(userId) {
|
|
26
79
|
return orderRepository.findByUser(userId);
|
|
27
80
|
}
|
|
@@ -96,6 +96,13 @@ export interface OrderDocumentItem {
|
|
|
96
96
|
prizeRevealDeadline?: string;
|
|
97
97
|
/** SB8-F â€" set after the reveal API picks a winner. */
|
|
98
98
|
revealedItemNumber?: number;
|
|
99
|
+
/** Set when the buyer cancels this specific line item post-order (partial cancellation). */
|
|
100
|
+
cancelledQuantity?: number;
|
|
101
|
+
cancelledAt?: Date;
|
|
102
|
+
cancelledReason?: string;
|
|
103
|
+
/** P-8 GST — snapshotted from the product at order time so the invoice stays accurate even if the product's HSN/rate later changes. */
|
|
104
|
+
hsnCode?: string;
|
|
105
|
+
gstRate?: 0 | 5 | 12 | 18 | 28;
|
|
99
106
|
}
|
|
100
107
|
/**
|
|
101
108
|
* One refund event on an order. Multiple partial refund events are allowed
|
|
@@ -173,6 +180,15 @@ export interface OrderDocument extends BaseDocument {
|
|
|
173
180
|
codRemainingAmount?: number;
|
|
174
181
|
/** COD handling fee charged to the buyer: max(codHandlingFeeMinInPaise, subtotal × codHandlingFeePercent / 100). Only set when paymentMethod === "cod". */
|
|
175
182
|
codHandlingFee?: number;
|
|
183
|
+
/** Order subtotal before GST — the amount GST is computed on. */
|
|
184
|
+
taxableAmount?: number;
|
|
185
|
+
/** Total GST charged (cgst + sgst, or igst — never both pairs at once). */
|
|
186
|
+
gstAmount?: number;
|
|
187
|
+
/** Intra-state half: buyer and seller/store pickup address share the same state. */
|
|
188
|
+
cgst?: number;
|
|
189
|
+
sgst?: number;
|
|
190
|
+
/** Inter-state: buyer and seller/store pickup address are in different states. */
|
|
191
|
+
igst?: number;
|
|
176
192
|
emiEnabled?: boolean;
|
|
177
193
|
/** Number of monthly installments the buyer chose (2–6). */
|
|
178
194
|
emiTenureMonths?: number;
|
|
@@ -36,6 +36,8 @@ export interface OrderItem {
|
|
|
36
36
|
prizeRevealDeadline?: string;
|
|
37
37
|
/** Set after the reveal endpoint picks a winner — the prize item index. */
|
|
38
38
|
revealedItemNumber?: number;
|
|
39
|
+
/** Set when the buyer cancels this specific line item post-order (partial cancellation). */
|
|
40
|
+
cancelledQuantity?: number;
|
|
39
41
|
/**
|
|
40
42
|
* SB-UNI-4 2026-05-13 — bundle identifier when this order line represents a
|
|
41
43
|
* bundle purchase. `productId` then points at the bundle category id and
|
|
@@ -195,6 +195,10 @@ export interface ProductDocument extends BaseDocument {
|
|
|
195
195
|
isSold?: boolean;
|
|
196
196
|
/** When true, an EMI order for this product ships as soon as it's confirmed instead of waiting for every installment to be paid. Default false. */
|
|
197
197
|
allowShipBeforeEmiComplete?: boolean;
|
|
198
|
+
/** P-8 GST — buyer-facing tax rate on this product (%). Unset/0 = exempt. */
|
|
199
|
+
gstRate?: 0 | 5 | 12 | 18 | 28;
|
|
200
|
+
/** P-8 GST — Harmonized System of Nomenclature code for GST-compliant invoices. */
|
|
201
|
+
hsnCode?: string;
|
|
198
202
|
/** Print-specific metadata for "art" / "stickers" listings — printed-only physical goods. Optional on every listing type; only populated for art/stickers. */
|
|
199
203
|
printMeta?: ProductPrintMeta;
|
|
200
204
|
promotionEndDate?: Date;
|
|
@@ -27,3 +27,15 @@ export declare function getPublicScammerById(id: string): Promise<ScammerDocumen
|
|
|
27
27
|
* Returns null if not found or not verified.
|
|
28
28
|
*/
|
|
29
29
|
export declare function getScammerProfilePageData(id: string): Promise<ScammerProfilePageData | null>;
|
|
30
|
+
export type SellerTrustStatus = "clear" | "flagged";
|
|
31
|
+
export interface SellerTrustResult {
|
|
32
|
+
status: SellerTrustStatus;
|
|
33
|
+
/** seoSlug of matched verified scammer profiles — links to /scams/[slug]. Empty when clear. */
|
|
34
|
+
matchedProfileSlugs: string[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* P-12 — resolves a store owner's phone/email against the verified (admin-published)
|
|
38
|
+
* scammer registry only. Never surfaces "under review" reports to buyers — a report
|
|
39
|
+
* that hasn't been verified must not damage a seller's storefront reputation.
|
|
40
|
+
*/
|
|
41
|
+
export declare function getSellerTrustStatus(storeId: string): Promise<SellerTrustResult>;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use server";
|
|
2
2
|
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
3
3
|
import { scammerRepository } from "../repository/scammer.repository";
|
|
4
|
+
import { userRepository } from "../../auth/repository/user.repository";
|
|
5
|
+
import { storeRepository } from "../../stores/repository/store.repository";
|
|
4
6
|
import { safeFireAndForget } from "../../../utils/safe-fire-forget";
|
|
5
7
|
/**
|
|
6
8
|
* List verified scammer profiles for the public registry page.
|
|
@@ -62,3 +64,32 @@ export async function getScammerProfilePageData(id) {
|
|
|
62
64
|
]);
|
|
63
65
|
return { scammer, incidents, comments, relatedScammers };
|
|
64
66
|
}
|
|
67
|
+
const TRUST_STATUS_CLEAR = { status: "clear", matchedProfileSlugs: [] };
|
|
68
|
+
/**
|
|
69
|
+
* P-12 — resolves a store owner's phone/email against the verified (admin-published)
|
|
70
|
+
* scammer registry only. Never surfaces "under review" reports to buyers — a report
|
|
71
|
+
* that hasn't been verified must not damage a seller's storefront reputation.
|
|
72
|
+
*/
|
|
73
|
+
export async function getSellerTrustStatus(storeId) {
|
|
74
|
+
const store = await storeRepository.findById(storeId).catch(() => null);
|
|
75
|
+
if (!store?.ownerId)
|
|
76
|
+
return TRUST_STATUS_CLEAR;
|
|
77
|
+
const owner = await userRepository.findById(store.ownerId).catch(() => null);
|
|
78
|
+
if (!owner)
|
|
79
|
+
return TRUST_STATUS_CLEAR;
|
|
80
|
+
const lookups = [];
|
|
81
|
+
if (owner.phoneNumber)
|
|
82
|
+
lookups.push(scammerRepository.findByContactField("phones", owner.phoneNumber));
|
|
83
|
+
if (owner.email)
|
|
84
|
+
lookups.push(scammerRepository.findByContactField("emails", owner.email));
|
|
85
|
+
if (lookups.length === 0)
|
|
86
|
+
return TRUST_STATUS_CLEAR;
|
|
87
|
+
const results = await Promise.all(lookups.map((p) => p.catch(() => [])));
|
|
88
|
+
const verified = results.flat().filter((s) => s.status === "verified");
|
|
89
|
+
if (verified.length === 0)
|
|
90
|
+
return TRUST_STATUS_CLEAR;
|
|
91
|
+
return {
|
|
92
|
+
status: "flagged",
|
|
93
|
+
matchedProfileSlugs: [...new Set(verified.map((s) => s.seoSlug))],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SellerTrustResult } from "../actions/scam-actions";
|
|
2
|
+
export interface SellerTrustBadgeProps {
|
|
3
|
+
trust: SellerTrustResult;
|
|
4
|
+
className?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* P-12 — renders next to a seller's storefront name. Only ever reflects
|
|
8
|
+
* admin-verified scammer profiles (see getSellerTrustStatus) — unverified
|
|
9
|
+
* reports never reach this component, so "clear" here means "no verified
|
|
10
|
+
* match," not "no reports filed."
|
|
11
|
+
*
|
|
12
|
+
* Named SellerTrustBadge (not TrustBadge) to avoid colliding with the
|
|
13
|
+
* unrelated homepage-section `TrustBadge` config type in
|
|
14
|
+
* features/homepage/index.ts (marketing trust-badge icons row).
|
|
15
|
+
*/
|
|
16
|
+
export declare function SellerTrustBadge({ trust, className }: SellerTrustBadgeProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Badge } from "../../../ui/components/Badge";
|
|
3
|
+
import { TextLink } from "../../../ui/components/TextLink";
|
|
4
|
+
import { ROUTES } from "../../../next";
|
|
5
|
+
/**
|
|
6
|
+
* P-12 — renders next to a seller's storefront name. Only ever reflects
|
|
7
|
+
* admin-verified scammer profiles (see getSellerTrustStatus) — unverified
|
|
8
|
+
* reports never reach this component, so "clear" here means "no verified
|
|
9
|
+
* match," not "no reports filed."
|
|
10
|
+
*
|
|
11
|
+
* Named SellerTrustBadge (not TrustBadge) to avoid colliding with the
|
|
12
|
+
* unrelated homepage-section `TrustBadge` config type in
|
|
13
|
+
* features/homepage/index.ts (marketing trust-badge icons row).
|
|
14
|
+
*/
|
|
15
|
+
export function SellerTrustBadge({ trust, className = "" }) {
|
|
16
|
+
if (trust.status === "clear") {
|
|
17
|
+
return (_jsx(Badge, { variant: "success", className: className, children: "\u2713 Verified Safe" }));
|
|
18
|
+
}
|
|
19
|
+
const firstSlug = trust.matchedProfileSlugs[0];
|
|
20
|
+
return (_jsx(Badge, { variant: "danger", className: className, children: firstSlug ? (_jsx(TextLink, { href: String(ROUTES.PUBLIC.SCAM_DETAIL(firstSlug)), children: "\u26A0 Flagged in Scam Registry" })) : ("⚠ Flagged in Scam Registry") }));
|
|
21
|
+
}
|
|
@@ -4,3 +4,5 @@ export { ScamProfileView } from "./ScamProfileView";
|
|
|
4
4
|
export type { ScamProfileViewProps } from "./ScamProfileView";
|
|
5
5
|
export { ScamAwarenessModal } from "./ScamAwarenessModal";
|
|
6
6
|
export type { ScamAwarenessModalProps } from "./ScamAwarenessModal";
|
|
7
|
+
export { SellerTrustBadge } from "./SellerTrustBadge";
|
|
8
|
+
export type { SellerTrustBadgeProps } from "./SellerTrustBadge";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
export interface SellerBundlesViewProps {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
getEditHref: (row: {
|
|
4
|
+
id: string;
|
|
5
|
+
}) => string;
|
|
6
|
+
newHref: string;
|
|
7
7
|
}
|
|
8
|
-
export declare function SellerBundlesView({
|
|
8
|
+
export declare function SellerBundlesView({ getEditHref, newHref }: SellerBundlesViewProps): React.JSX.Element;
|
|
@@ -1,111 +1,108 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
|
|
6
|
-
import { ConfirmDeleteModal, RowActionMenu, Text } from "../../../ui";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { SIEVE_OP, sieveFilter, sortBy } from "@mohasinac/appkit";
|
|
4
|
+
import { Badge, Button, FilterChipGroup, Stack, Text, TextLink } from "../../../ui";
|
|
7
5
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
import { toRecordArray, toRelativeDate, toRupees, toStringValue, } from "../../admin/hooks/useAdminListingData";
|
|
6
|
+
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
|
|
7
|
+
import { SELLER_BULK_ACTIONS } from "../../products/constants/action-defs";
|
|
8
|
+
import { BUNDLE_COPY, BUNDLE_STOCK_VARIANT, } from "../../../_internal/shared/features/categories/bundle-copy";
|
|
9
|
+
import { toRecordArray, toRelativeDate, toStringValue, } from "../../admin/hooks/useAdminListingData";
|
|
13
10
|
import { DataListingView } from "../../admin/components/DataListingView";
|
|
11
|
+
function formatPrice(paise) {
|
|
12
|
+
if (typeof paise !== "number" || paise <= 0)
|
|
13
|
+
return "—";
|
|
14
|
+
return `₹${Math.round(paise / 100).toLocaleString("en-IN")}`;
|
|
15
|
+
}
|
|
14
16
|
const COLUMNS = [
|
|
15
|
-
{ key: "title", header: "Bundle", render: (row) => _jsx(Text, { size: "sm", weight: "medium", children: row.title }) },
|
|
16
|
-
{ key: "price", header: "Price", render: (row) => _jsx(Text, { className: "tabular-nums", size: "sm", children: row.price }) },
|
|
17
17
|
{
|
|
18
|
-
key: "
|
|
19
|
-
header: "
|
|
20
|
-
render: (row) => (_jsx(
|
|
18
|
+
key: "primary",
|
|
19
|
+
header: "Name",
|
|
20
|
+
render: (row) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { weight: "medium", color: "primary", children: row.primary }), _jsx(Text, { size: "xs", color: "muted", children: row.secondary })] })),
|
|
21
21
|
},
|
|
22
|
+
{ key: "price", header: "Price", className: "w-28" },
|
|
23
|
+
{ key: "members", header: "Members", className: "w-24" },
|
|
22
24
|
{
|
|
23
|
-
key: "
|
|
24
|
-
header: "
|
|
25
|
-
|
|
25
|
+
key: "stockStatus",
|
|
26
|
+
header: "Stock",
|
|
27
|
+
className: "w-28",
|
|
28
|
+
render: (row) => (_jsx(Badge, { variant: BUNDLE_STOCK_VARIANT[row.stockStatus] ?? "default", children: row.stockStatus === "in_stock"
|
|
29
|
+
? BUNDLE_COPY.stockBadge.listVariantInStock
|
|
30
|
+
: BUNDLE_COPY.stockBadge.listVariantOutOfStock })),
|
|
26
31
|
},
|
|
27
32
|
{
|
|
28
|
-
key: "
|
|
29
|
-
header: "
|
|
30
|
-
|
|
33
|
+
key: "status",
|
|
34
|
+
header: "Status",
|
|
35
|
+
className: "w-24",
|
|
36
|
+
render: (row) => (_jsx(Badge, { variant: row.isActive ? "success" : "default", children: row.status })),
|
|
31
37
|
},
|
|
38
|
+
{ key: "updatedAt", header: "Updated", className: "w-28" },
|
|
32
39
|
];
|
|
33
|
-
export function SellerBundlesView({
|
|
34
|
-
const [deleteTargetId, setDeleteTargetId] = useState(null);
|
|
35
|
-
const { deletingId, handleDelete: performDelete } = useEntityDelete({
|
|
36
|
-
endpoint: SELLER_ENDPOINTS.PRODUCT_BY_ID,
|
|
37
|
-
deleteFn: onDelete,
|
|
38
|
-
successMessage: "Bundle deleted.",
|
|
39
|
-
fetchOptions: { credentials: "include" },
|
|
40
|
-
});
|
|
41
|
-
const handleDelete = useCallback(async (id) => {
|
|
42
|
-
await performDelete(id);
|
|
43
|
-
setDeleteTargetId(null);
|
|
44
|
-
}, [performDelete]);
|
|
45
|
-
const handleEdit = useCallback((id) => {
|
|
46
|
-
if (onEditClick)
|
|
47
|
-
onEditClick(id);
|
|
48
|
-
else
|
|
49
|
-
window.location.href = String(ROUTES.STORE.PRODUCTS_EDIT(id));
|
|
50
|
-
}, [onEditClick]);
|
|
51
|
-
const handleCreate = useCallback(() => {
|
|
52
|
-
if (onCreateClick)
|
|
53
|
-
onCreateClick();
|
|
54
|
-
else
|
|
55
|
-
window.location.href = String(ROUTES.STORE.PRODUCTS_NEW);
|
|
56
|
-
}, [onCreateClick]);
|
|
40
|
+
export function SellerBundlesView({ getEditHref, newHref }) {
|
|
57
41
|
const config = {
|
|
58
42
|
portal: "seller",
|
|
59
43
|
title: "Bundles",
|
|
60
|
-
searchPlaceholder: "Search bundles
|
|
61
|
-
emptyLabel: "No bundles yet — create a bundle to group multiple products together",
|
|
62
|
-
filterKeys: [],
|
|
63
|
-
defaultSort: sortBy("
|
|
64
|
-
queryKey: ["seller", "bundles"],
|
|
65
|
-
endpoint: SELLER_ENDPOINTS.
|
|
44
|
+
searchPlaceholder: "Search bundles by name or slug…",
|
|
45
|
+
emptyLabel: "No bundles yet — create a bundle to group multiple of your products together",
|
|
46
|
+
filterKeys: ["isActive", "bundleStockStatus"],
|
|
47
|
+
defaultSort: sortBy("name", "ASC"),
|
|
48
|
+
queryKey: ["seller", "bundles", "listing"],
|
|
49
|
+
endpoint: SELLER_ENDPOINTS.BUNDLES,
|
|
66
50
|
sortOptions: [
|
|
51
|
+
{ value: sortBy("name", "ASC"), label: "Name A–Z" },
|
|
52
|
+
{ value: sortBy("name", "DESC"), label: "Name Z–A" },
|
|
53
|
+
{ value: sortBy("bundlePriceInPaise", "DESC"), label: "Price high→low" },
|
|
54
|
+
{ value: "bundlePriceInPaise", label: "Price low→high" },
|
|
67
55
|
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
68
56
|
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
69
|
-
{ value: "productTitle", label: "Name A–Z" },
|
|
70
|
-
{ value: sortBy("productTitle", "DESC"), label: "Name Z–A" },
|
|
71
|
-
{ value: sortBy("price", "ASC"), label: "Price: Low–High" },
|
|
72
|
-
{ value: sortBy("price", "DESC"), label: "Price: High–Low" },
|
|
73
57
|
],
|
|
74
58
|
columns: COLUMNS,
|
|
75
|
-
mapRows: (response) => toRecordArray(response.
|
|
59
|
+
mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
|
|
76
60
|
id: toStringValue(item.id, `bundle-${index}`),
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
61
|
+
primary: toStringValue(item.name, "Untitled bundle"),
|
|
62
|
+
secondary: toStringValue(item.slug, "no-slug"),
|
|
63
|
+
price: formatPrice(item.bundlePriceInPaise),
|
|
64
|
+
members: String(Array.isArray(item.bundleProductIds) ? item.bundleProductIds.length : 0),
|
|
65
|
+
stockStatus: toStringValue(item.bundleStockStatus, "in_stock"),
|
|
66
|
+
isActive: item.isActive === true,
|
|
67
|
+
status: item.isActive === true
|
|
68
|
+
? BUNDLE_COPY.adminList.activeBadge
|
|
69
|
+
: BUNDLE_COPY.adminList.inactiveBadge,
|
|
70
|
+
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
84
71
|
})),
|
|
85
|
-
getTotal: (response, mappedRows) => typeof response.
|
|
86
|
-
buildFilters: () =>
|
|
87
|
-
|
|
72
|
+
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
73
|
+
buildFilters: (filterState) => {
|
|
74
|
+
const parts = [];
|
|
75
|
+
if (filterState.isActive)
|
|
76
|
+
parts.push(sieveFilter("isActive", SIEVE_OP.EQ, filterState.isActive));
|
|
77
|
+
if (filterState.bundleStockStatus)
|
|
78
|
+
parts.push(sieveFilter("bundleStockStatus", SIEVE_OP.EQ, filterState.bundleStockStatus));
|
|
79
|
+
return parts.join(",") || undefined;
|
|
80
|
+
},
|
|
81
|
+
getRowHref: getEditHref,
|
|
82
|
+
toolbarExtra: (_jsx(Button, { asChild: true, size: "sm", variant: "primary", children: _jsx(TextLink, { href: newHref, layout: "flex", align: "center", gap: "xs", children: "+ New Bundle" }) })),
|
|
88
83
|
// Rule #7: bulk-action array sourced from the SELLER_BULK_ACTIONS preset.
|
|
89
|
-
buildBulkActions:
|
|
90
|
-
|
|
84
|
+
buildBulkActions: (selection) => {
|
|
85
|
+
const handlers = {
|
|
86
|
+
[ROW_ACTION_ID.DELETE]: async () => {
|
|
87
|
+
await Promise.all(selection.selectedIds.map((id) => fetch(SELLER_ENDPOINTS.BUNDLE_BY_ID(id), { method: "DELETE" })));
|
|
88
|
+
selection.clearSelection();
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
return SELLER_BULK_ACTIONS.bundles.map((id) => ({
|
|
91
92
|
id,
|
|
92
93
|
label: ROW_ACTION_META[id].label,
|
|
93
94
|
destructive: ROW_ACTION_META[id].destructive,
|
|
94
|
-
onClick:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
onClick: () => setDeleteTargetId(row.id),
|
|
106
|
-
disabled: deletingId === row.id,
|
|
107
|
-
},
|
|
108
|
-
] })),
|
|
95
|
+
onClick: handlers[id],
|
|
96
|
+
}));
|
|
97
|
+
},
|
|
98
|
+
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: [
|
|
99
|
+
{ id: "All", label: "All" },
|
|
100
|
+
{ id: "true", label: "Active" },
|
|
101
|
+
{ id: "false", label: "Inactive" },
|
|
102
|
+
], value: pendingFilters.isActive || "", onChange: (v) => setPendingFilters((p) => ({ ...p, isActive: v })) }), _jsx(FilterChipGroup, { label: "Stock", tabs: [
|
|
103
|
+
{ id: "All", label: "All" },
|
|
104
|
+
{ id: "out_of_stock", label: "Sold out" },
|
|
105
|
+
], value: pendingFilters.bundleStockStatus || "", onChange: (v) => setPendingFilters((p) => ({ ...p, bundleStockStatus: v })) })] })),
|
|
109
106
|
};
|
|
110
|
-
return
|
|
107
|
+
return _jsx(DataListingView, { config: config });
|
|
111
108
|
}
|
|
@@ -190,7 +190,13 @@ function StepShipping({ values, onChange, renderAddressSelector, }) {
|
|
|
190
190
|
] }), renderAddressSelector ? (_jsxs(_Fragment, { children: [_jsx(Text, { className: "text-[var(--appkit-color-text)] mb-1", size: "sm", weight: "medium", children: "Pickup Address (optional)" }), renderAddressSelector({
|
|
191
191
|
value: values.pickupAddressId ?? "",
|
|
192
192
|
onChange: (v) => onChange({ pickupAddressId: v }),
|
|
193
|
-
})] })) : (_jsx(StoreAddressSelectorCreate, { label: "Pickup Address (optional)", value: values.pickupAddressId ?? "", onChange: (id) => onChange({ pickupAddressId: id }) })), _jsx(Toggle, { checked: !!values.insurance, onChange: (checked) => onChange({ insurance: checked, insuranceCost: checked ? values.insuranceCost ?? 0 : undefined }), label: "Offer shipping insurance" }), values.insurance && (_jsx(FormField, { name: "insuranceCost", label: "Insurance Cost (\u20B9)", type: "number", value: toRupees(values.insuranceCost), onChange: (v) => onChange({ insuranceCost: toPaise(v) }), placeholder: "0" }))
|
|
193
|
+
})] })) : (_jsx(StoreAddressSelectorCreate, { label: "Pickup Address (optional)", value: values.pickupAddressId ?? "", onChange: (id) => onChange({ pickupAddressId: id }) })), _jsx(Toggle, { checked: !!values.insurance, onChange: (checked) => onChange({ insurance: checked, insuranceCost: checked ? values.insuranceCost ?? 0 : undefined }), label: "Offer shipping insurance" }), values.insurance && (_jsx(FormField, { name: "insuranceCost", label: "Insurance Cost (\u20B9)", type: "number", value: toRupees(values.insuranceCost), onChange: (v) => onChange({ insuranceCost: toPaise(v) }), placeholder: "0" })), _jsx(FormField, { name: "gstRate", label: "GST Rate", type: "select", value: String(values.gstRate ?? 0), onChange: (v) => onChange({ gstRate: Number(v) }), options: [
|
|
194
|
+
{ value: "0", label: "Exempt (0%)" },
|
|
195
|
+
{ value: "5", label: "5%" },
|
|
196
|
+
{ value: "12", label: "12%" },
|
|
197
|
+
{ value: "18", label: "18%" },
|
|
198
|
+
{ value: "28", label: "28%" },
|
|
199
|
+
] }), _jsx(FormField, { name: "hsnCode", label: "HSN Code", value: values.hsnCode ?? "", onChange: (v) => onChange({ hsnCode: v }), placeholder: "e.g. 9503" })] }));
|
|
194
200
|
}
|
|
195
201
|
// ── Step: Publish / SEO ───────────────────────────────────────────────────
|
|
196
202
|
function StepPublish({ values, onChange, }) {
|
|
@@ -6,5 +6,12 @@ export interface StoreDetailLayoutViewProps {
|
|
|
6
6
|
/** The current active tab value: "products" | "auctions" | "reviews" | "about" */
|
|
7
7
|
activeTab: string;
|
|
8
8
|
children: ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* P-12 — gates the trust badge behind the same FEATURE_SCAM_REGISTRY flag
|
|
11
|
+
* that gates the registry itself (pending legal sign-off). Consumer passes
|
|
12
|
+
* `getFlag("SCAM_REGISTRY")`; appkit has no access to that env reader.
|
|
13
|
+
* Defaults to `false` — off unless the consumer explicitly opts in.
|
|
14
|
+
*/
|
|
15
|
+
scamRegistryEnabled?: boolean;
|
|
9
16
|
}
|
|
10
|
-
export declare function StoreDetailLayoutView({ storeSlug, activeTab, children, }: StoreDetailLayoutViewProps): Promise<React.JSX.Element>;
|
|
17
|
+
export declare function StoreDetailLayoutView({ storeSlug, activeTab, children, scamRegistryEnabled, }: StoreDetailLayoutViewProps): Promise<React.JSX.Element>;
|
|
@@ -6,6 +6,7 @@ import { ROUTES } from "../../../next";
|
|
|
6
6
|
import { Container, Main, Section, Text } from "../../../ui";
|
|
7
7
|
import { STORE_PAGE_TABS } from "../../products/constants/listing-tabs";
|
|
8
8
|
import { isListingTypeEnabled, isCategoryTypeEnabled } from "../../../_internal/shared/listing-types/feature-flags";
|
|
9
|
+
import { getSellerTrustStatus } from "../../scams/actions/scam-actions";
|
|
9
10
|
import { StoreHeader } from "./StoreHeader";
|
|
10
11
|
import { StoreNavTabs } from "./StoreNavTabs";
|
|
11
12
|
const STORE_LISTING_HREF = {
|
|
@@ -28,13 +29,18 @@ function tabLabel(base, count) {
|
|
|
28
29
|
return base;
|
|
29
30
|
return `${base} (${count.toLocaleString()})`;
|
|
30
31
|
}
|
|
31
|
-
export async function StoreDetailLayoutView({ storeSlug, activeTab, children, }) {
|
|
32
|
+
export async function StoreDetailLayoutView({ storeSlug, activeTab, children, scamRegistryEnabled = false, }) {
|
|
32
33
|
const store = await getStoreBySlug(storeSlug);
|
|
33
34
|
if (!store) {
|
|
34
35
|
return (_jsx(Main, { children: _jsx(Section, { padding: "y-5xl", children: _jsx(Container, { size: "md", children: _jsx(Text, { align: "start", color: "muted", children: "Store not found." }) }) }) }));
|
|
35
36
|
}
|
|
36
37
|
const storeId = store?.id;
|
|
37
|
-
const settings = await
|
|
38
|
+
const [settings, trust] = await Promise.all([
|
|
39
|
+
siteSettingsRepository.findById("global").catch(() => null),
|
|
40
|
+
scamRegistryEnabled && storeId
|
|
41
|
+
? getSellerTrustStatus(storeId).catch(() => undefined)
|
|
42
|
+
: Promise.resolve(undefined),
|
|
43
|
+
]);
|
|
38
44
|
const [productsCount, auctionsCount, preOrdersCount, prizeDrawsCount, bundlesCount, classifiedsCount, digitalCodesCount, liveCount, artCount, stickersCount, couponsCount, reviewsCount] = storeId
|
|
39
45
|
? await Promise.all([
|
|
40
46
|
productRepository
|
|
@@ -128,5 +134,5 @@ export async function StoreDetailLayoutView({ storeSlug, activeTab, children, })
|
|
|
128
134
|
{ value: "reviews", label: tabLabel("Reviews", reviewsCount), href: String(ROUTES.PUBLIC.STORE_REVIEWS(storeSlug)) },
|
|
129
135
|
{ value: "about", label: "About", href: String(ROUTES.PUBLIC.STORE_ABOUT(storeSlug)) },
|
|
130
136
|
];
|
|
131
|
-
return (_jsxs(Main, { children: [_jsx(StoreHeader, { store: store }), _jsxs(Container, { size: "xl", className: "mt-6", children: [_jsx(StoreNavTabs, { tabs: tabs, activeValue: activeTab }), _jsx(Section, { padding: "t-lg", children: children })] })] }));
|
|
137
|
+
return (_jsxs(Main, { children: [_jsx(StoreHeader, { store: store, trust: trust }), _jsxs(Container, { size: "xl", className: "mt-6", children: [_jsx(StoreNavTabs, { tabs: tabs, activeValue: activeTab }), _jsx(Section, { padding: "t-lg", children: children })] })] }));
|
|
132
138
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SellerTrustResult } from "../../scams/actions/scam-actions";
|
|
1
2
|
import type { StoreDetail } from "../types";
|
|
2
3
|
interface StoreHeaderProps {
|
|
3
4
|
store: StoreDetail;
|
|
@@ -10,6 +11,8 @@ interface StoreHeaderProps {
|
|
|
10
11
|
};
|
|
11
12
|
onFollow?: (storeSlug: string) => void;
|
|
12
13
|
className?: string;
|
|
14
|
+
/** P-12 — omitted (undefined) when the scam registry feature is off; no badge renders. */
|
|
15
|
+
trust?: SellerTrustResult;
|
|
13
16
|
}
|
|
14
|
-
export declare function StoreHeader({ store, labels, onFollow, className, }: StoreHeaderProps): import("react").JSX.Element;
|
|
17
|
+
export declare function StoreHeader({ store, labels, onFollow, className, trust, }: StoreHeaderProps): import("react").JSX.Element;
|
|
15
18
|
export {};
|
|
@@ -4,6 +4,7 @@ import { MediaImage } from "../../media/MediaImage";
|
|
|
4
4
|
import { normalizeRichTextHtml } from "../../../utils/string.formatter";
|
|
5
5
|
import { ShareButton } from "../../products/components/ShareButton";
|
|
6
6
|
import { StoreScopedSearch } from "./StoreScopedSearch";
|
|
7
|
+
import { SellerTrustBadge } from "../../scams/components/SellerTrustBadge";
|
|
7
8
|
const __O = {
|
|
8
9
|
hidden: "overflow-hidden",
|
|
9
10
|
};
|
|
@@ -11,8 +12,8 @@ const CLS_AVATAR = "-mt-8 h-16 w-16 rounded-xl border-2 border-white border-[var
|
|
|
11
12
|
const CLS_STARS = "inline-flex items-center gap-[var(--appkit-space-1)] text-warning";
|
|
12
13
|
const CLS_FOLLOW_BTN = "rounded-lg border border-warning px-[var(--appkit-space-4)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] font-medium text-warning hover:bg-warning-surface transition-colors";
|
|
13
14
|
const CLS_WARN_BANNER = "mt-3 rounded-lg bg-warning-surface dark:bg-warning-surface border border-warning dark:border-warning px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-warning dark:text-warning";
|
|
14
|
-
export function StoreHeader({ store, labels = {}, onFollow, className = "", }) {
|
|
15
|
-
return (_jsxs(Section, { surface: "default", border: "bottom", className: className, children: [store.storeBannerURL && (_jsx(Div, { className: `relative h-40 md:h-56 ${__O.hidden} bg-[var(--appkit-color-surface)] bg-[var(--appkit-color-surface-elevated)]`, children: _jsx(MediaImage, { src: store.storeBannerURL, alt: `${store.storeName} banner`, size: "banner" }) })), _jsxs(Div, { paddingX: "x-page", className: "max-w-7xl mx-auto", padding: "y-md", children: [_jsxs(Row, { align: "end", gap: "md", children: [store.storeLogoURL ? (_jsx(Div, { className: "relative -mt-8 h-16 w-16 border-2 border-white", rounded: "xl", shadow: "sm", overflow: "hidden", children: _jsx(MediaImage, { src: store.storeLogoURL, alt: store.storeName, size: "avatar" }) })) : (_jsx(Div, { className: CLS_AVATAR, children: store.storeName[0]?.toUpperCase() })), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { className: "mb-0.5", align: "center", gap: "sm", wrap: true, children: [_jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)]", size: "xl", weight: "bold", children: store.storeName }), store.averageRating != null && store.averageRating > 0 && (_jsxs(Span, { size: "sm", weight: "medium", className: CLS_STARS, children: ["\u2605 ", store.averageRating.toFixed(1)] }))] }), _jsxs(Row, { textSize: "xs", className: "text-[var(--appkit-color-text-muted)] mb-0.5", gap: "3", children: [store.category && _jsx(Span, { transform: "capitalize", children: store.category }), store.totalProducts != null && store.totalProducts > 0 && (_jsxs(Span, { children: [store.totalProducts, " ", labels.products ?? "products"] })), store.totalReviews != null && store.totalReviews > 0 && (_jsxs(Span, { children: [store.totalReviews, " ", labels.reviews ?? "reviews"] })), store.itemsSold != null && store.itemsSold > 0 && (_jsxs(Span, { children: [store.itemsSold, " ", labels.sold ?? "sold"] }))] }), store.storeDescription && (_jsx(RichText, { html: normalizeRichTextHtml(store.storeDescription), copyableCode: true, className: "mt-0.5" }))] }), _jsxs(Row, { gap: "sm", align: "center", className: "shrink-0", children: [_jsx(ShareButton, { title: store.storeName }), onFollow && (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onFollow(store.storeSlug), className: CLS_FOLLOW_BTN, children: labels.follow ?? "Follow" }))] })] }), store.isVacationMode && (_jsx(Text, { className: CLS_WARN_BANNER, children: store.vacationMessage ??
|
|
15
|
+
export function StoreHeader({ store, labels = {}, onFollow, className = "", trust, }) {
|
|
16
|
+
return (_jsxs(Section, { surface: "default", border: "bottom", className: className, children: [store.storeBannerURL && (_jsx(Div, { className: `relative h-40 md:h-56 ${__O.hidden} bg-[var(--appkit-color-surface)] bg-[var(--appkit-color-surface-elevated)]`, children: _jsx(MediaImage, { src: store.storeBannerURL, alt: `${store.storeName} banner`, size: "banner" }) })), _jsxs(Div, { paddingX: "x-page", className: "max-w-7xl mx-auto", padding: "y-md", children: [_jsxs(Row, { align: "end", gap: "md", children: [store.storeLogoURL ? (_jsx(Div, { className: "relative -mt-8 h-16 w-16 border-2 border-white", rounded: "xl", shadow: "sm", overflow: "hidden", children: _jsx(MediaImage, { src: store.storeLogoURL, alt: store.storeName, size: "avatar" }) })) : (_jsx(Div, { className: CLS_AVATAR, children: store.storeName[0]?.toUpperCase() })), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { className: "mb-0.5", align: "center", gap: "sm", wrap: true, children: [_jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)]", size: "xl", weight: "bold", children: store.storeName }), store.averageRating != null && store.averageRating > 0 && (_jsxs(Span, { size: "sm", weight: "medium", className: CLS_STARS, children: ["\u2605 ", store.averageRating.toFixed(1)] })), trust && _jsx(SellerTrustBadge, { trust: trust })] }), _jsxs(Row, { textSize: "xs", className: "text-[var(--appkit-color-text-muted)] mb-0.5", gap: "3", children: [store.category && _jsx(Span, { transform: "capitalize", children: store.category }), store.totalProducts != null && store.totalProducts > 0 && (_jsxs(Span, { children: [store.totalProducts, " ", labels.products ?? "products"] })), store.totalReviews != null && store.totalReviews > 0 && (_jsxs(Span, { children: [store.totalReviews, " ", labels.reviews ?? "reviews"] })), store.itemsSold != null && store.itemsSold > 0 && (_jsxs(Span, { children: [store.itemsSold, " ", labels.sold ?? "sold"] }))] }), store.storeDescription && (_jsx(RichText, { html: normalizeRichTextHtml(store.storeDescription), copyableCode: true, className: "mt-0.5" }))] }), _jsxs(Row, { gap: "sm", align: "center", className: "shrink-0", children: [_jsx(ShareButton, { title: store.storeName }), onFollow && (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onFollow(store.storeSlug), className: CLS_FOLLOW_BTN, children: labels.follow ?? "Follow" }))] })] }), store.isVacationMode && (_jsx(Text, { className: CLS_WARN_BANNER, children: store.vacationMessage ??
|
|
16
17
|
labels.vacationMode ??
|
|
17
18
|
"Store is on vacation mode" })), _jsx(Div, { className: "mt-3", children: _jsx(StoreScopedSearch, { storeId: store.storeSlug, storeName: store.storeName }) })] })] }));
|
|
18
19
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2279,6 +2279,7 @@ export type { PaymentGateway } from "./features/orders/index";
|
|
|
2279
2279
|
export type { RefundStatus } from "./features/orders/index";
|
|
2280
2280
|
export type { ShippingMethod } from "./features/orders/index";
|
|
2281
2281
|
export { cancelOrderForUser } from "./features/orders/server";
|
|
2282
|
+
export { cancelOrderItemsForUser } from "./features/orders/server";
|
|
2282
2283
|
export { getOrderByIdForUser } from "./features/orders/server";
|
|
2283
2284
|
export { getOrderTrackingHandler } from "./features/orders/server";
|
|
2284
2285
|
export { getTrackingInfo } from "./features/orders/server";
|
|
@@ -2450,8 +2451,8 @@ export { actionTracker, setActionTrackerSink, resetActionTrackerSink, type Actio
|
|
|
2450
2451
|
export { cartRequiresShipping, cartIsDigitalOnly, cartIsChatOnly, } from "./_internal/shared/listing-types/cart-shipping";
|
|
2451
2452
|
export { ACTIONS, action, act, canPerformAction, actionsForListingType, actionLabel, type ActionDef, type ActionKind, type ActionResource, type ActionTree, type ActionConfirmation, } from "./_internal/shared/actions/action-registry";
|
|
2452
2453
|
export { buildBulkAction } from "./_internal/shared/actions/bulk-helpers";
|
|
2453
|
-
export { computeCheckoutFees, computePayoutDeduction, computeCodHandlingFee, } from "./_internal/shared/fees/calculator";
|
|
2454
|
-
export type { FeeCommissionRates, CheckoutFees, PayoutDeduction, CodHandlingFeeRates, } from "./_internal/shared/fees/calculator";
|
|
2454
|
+
export { computeCheckoutFees, computePayoutDeduction, computeCodHandlingFee, calculateGst, } from "./_internal/shared/fees/calculator";
|
|
2455
|
+
export type { FeeCommissionRates, CheckoutFees, PayoutDeduction, CodHandlingFeeRates, GstBreakdown, } from "./_internal/shared/fees/calculator";
|
|
2455
2456
|
export { checkEmiEligibility, computeEmiSchedule } from "./_internal/shared/features/emi/schedule";
|
|
2456
2457
|
export type { EmiSettings, EmiIneligibleReason, EmiEligibility, EmiInstallmentPlan, EmiScheduleResult, } from "./_internal/shared/features/emi/schedule";
|
|
2457
2458
|
export { isAdminUser, isSellerUser, isModeratorUser, isEmployeeUser, isBuyerUser, } from "./features/auth/role-predicates";
|
|
@@ -3142,9 +3143,12 @@ export { ScamProfileView } from "./features/scams/components/ScamProfileView";
|
|
|
3142
3143
|
export type { ScamProfileViewProps } from "./features/scams/components/ScamProfileView";
|
|
3143
3144
|
export { ScamAwarenessModal } from "./features/scams/components/ScamAwarenessModal";
|
|
3144
3145
|
export type { ScamAwarenessModalProps } from "./features/scams/components/ScamAwarenessModal";
|
|
3145
|
-
export {
|
|
3146
|
+
export { SellerTrustBadge } from "./features/scams/components/SellerTrustBadge";
|
|
3147
|
+
export type { SellerTrustBadgeProps } from "./features/scams/components/SellerTrustBadge";
|
|
3148
|
+
export { listVerifiedScammers, getPublicScammerById, getScammerProfilePageData, getSellerTrustStatus, } from "./features/scams/actions/scam-actions";
|
|
3146
3149
|
export type { ScammerProfilePageData } from "./features/scams/actions/scam-actions";
|
|
3147
3150
|
export type { ScammerListResult } from "./features/scams/actions/scam-actions";
|
|
3151
|
+
export type { SellerTrustResult, SellerTrustStatus } from "./features/scams/actions/scam-actions";
|
|
3148
3152
|
export { SellerProductShell } from "./features/seller/components/SellerProductShell";
|
|
3149
3153
|
export type { SellerProductShellProps, SellerProductDraft, ProductListingMode } from "./features/seller/components/SellerProductShell";
|
|
3150
3154
|
export { CategoryInlineSelect } from "./features/seller/components/CategoryInlineSelect";
|