@mohasinac/appkit 2.6.6 → 2.6.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/client.d.ts CHANGED
@@ -151,8 +151,8 @@ export type { SellerDashboardViewProps as StoreDashboardViewProps, SellerDashboa
151
151
  export { SellerPayoutSettingsView, SellerShippingView, SellerReviewsView, SellerPayoutRequestView, SellerAnalyticsStats, SellerTopProducts, SellerAnalyticsView, SellerPayoutsView, SellerCouponEditorView, SellerBidsView, SellerAddressesView, SellerPreOrdersView, SellerPrizeDrawsView } from "./features/seller/components/index";
152
152
  export type { SellerPayoutSettingsViewProps, SellerShippingViewProps, SellerReviewsViewProps, SellerPayoutRequestViewProps, SellerAnalyticsViewProps, SellerPayoutsViewProps, SellerCouponEditorViewProps, CouponEditorDraft, SellerBidsViewProps, SellerAddressesViewProps, SellerPreOrdersViewProps, SellerPrizeDrawsViewProps } from "./features/seller/components/index";
153
153
  export type { SellerAnalyticsSummary, SellerAnalyticsTopProduct } from "./features/seller/types/index";
154
- export { UserAccountHubView, UserOrdersView, OrderDetailView, UserNotificationsView, UserReturnsView } from "./features/account/index";
155
- export type { UserAccountHubViewProps, UserAccountHubViewLabels, UserOrdersViewProps, UserOrdersViewLabels, OrderDetailViewProps, OrderDetailViewLabels, UserNotificationsViewProps, UserNotificationsViewLabels, UserReturnsViewProps, UserReturnsViewLabels } from "./features/account/index";
154
+ export { UserAccountHubView, UserOrdersView, OrderDetailView, UserNotificationsView, UserReturnsView, UserSupportView } from "./features/account/index";
155
+ export type { UserAccountHubViewProps, UserAccountHubViewLabels, UserOrdersViewProps, UserOrdersViewLabels, OrderDetailViewProps, OrderDetailViewLabels, UserNotificationsViewProps, UserNotificationsViewLabels, UserReturnsViewProps, UserReturnsViewLabels, UserSupportViewProps } from "./features/account/index";
156
156
  export { useOrders, useOrder, OrdersList } from "./features/orders/index";
157
157
  export { useCouponValidate } from "./features/promotions/hooks/useCouponValidate";
158
158
  export { BlogPostView } from "./features/blog/components/BlogPostView";
package/dist/client.js CHANGED
@@ -181,7 +181,7 @@ export { SellerOffersPanel } from "./features/seller/components/SellerOffersPane
181
181
  export { UserOffersPanel } from "./features/account/components/UserOffersPanel";
182
182
  export { SellerDashboardView as StoreDashboardView, SellerDashboardView, useSellerDashboard as useStoreDashboard, useSellerDashboard } from "./features/seller/index";
183
183
  export { SellerPayoutSettingsView, SellerShippingView, SellerReviewsView, SellerPayoutRequestView, SellerAnalyticsStats, SellerTopProducts, SellerAnalyticsView, SellerPayoutsView, SellerCouponEditorView, SellerBidsView, SellerAddressesView, SellerPreOrdersView, SellerPrizeDrawsView } from "./features/seller/components/index";
184
- export { UserAccountHubView, UserOrdersView, OrderDetailView, UserNotificationsView, UserReturnsView } from "./features/account/index";
184
+ export { UserAccountHubView, UserOrdersView, OrderDetailView, UserNotificationsView, UserReturnsView, UserSupportView } from "./features/account/index";
185
185
  export { useOrders, useOrder, OrdersList } from "./features/orders/index";
186
186
  export { useCouponValidate } from "./features/promotions/hooks/useCouponValidate";
187
187
  export { BlogPostView } from "./features/blog/components/BlogPostView";
@@ -0,0 +1,3 @@
1
+ export interface UserSupportViewProps {
2
+ }
3
+ export declare function UserSupportView(_props: UserSupportViewProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,90 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
5
+ import { Button, Div, FormActions, Select, SideDrawer, Text, useToast, } from "../../../ui";
6
+ import { apiClient } from "../../../http";
7
+ import { SUPPORT_ENDPOINTS } from "../../../constants/api-endpoints";
8
+ // --- Constants ---------------------------------------------------------------
9
+ const CATEGORY_OPTIONS = [
10
+ { label: "Order Issue", value: "order_issue" },
11
+ { label: "Billing / Payment", value: "billing_payment" },
12
+ { label: "Account", value: "account" },
13
+ { label: "Listing Dispute", value: "listing_dispute" },
14
+ { label: "Refund Request", value: "refund_request" },
15
+ { label: "Auction Dispute", value: "auction_dispute" },
16
+ { label: "General", value: "general" },
17
+ ];
18
+ const STATUS_BADGE = {
19
+ open: "bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300",
20
+ in_progress: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/40 dark:text-yellow-300",
21
+ waiting_on_user: "bg-purple-100 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300",
22
+ resolved: "bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300",
23
+ closed: "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400",
24
+ };
25
+ const ROLE_LABEL = {
26
+ user: "You",
27
+ support: "Support",
28
+ admin: "Admin",
29
+ };
30
+ export function UserSupportView(_props) {
31
+ const queryClient = useQueryClient();
32
+ const { showToast } = useToast();
33
+ const [detailOpen, setDetailOpen] = useState(false);
34
+ const [selectedTicket, setSelectedTicket] = useState(null);
35
+ const [newTicketOpen, setNewTicketOpen] = useState(false);
36
+ // New ticket form state
37
+ const [newSubject, setNewSubject] = useState("");
38
+ const [newCategory, setNewCategory] = useState("general");
39
+ const [newDescription, setNewDescription] = useState("");
40
+ const [newOrderId, setNewOrderId] = useState("");
41
+ // Reply state
42
+ const [replyBody, setReplyBody] = useState("");
43
+ const { data, isLoading, error } = useQuery({
44
+ queryKey: ["user", "support-tickets"],
45
+ queryFn: () => apiClient.get(SUPPORT_ENDPOINTS.TICKETS),
46
+ });
47
+ const tickets = data?.tickets ?? [];
48
+ const invalidate = () => queryClient.invalidateQueries({ queryKey: ["user", "support-tickets"] });
49
+ const createMutation = useMutation({
50
+ mutationFn: async () => {
51
+ await apiClient.post(SUPPORT_ENDPOINTS.TICKETS, {
52
+ subject: newSubject.trim(),
53
+ category: newCategory,
54
+ description: newDescription.trim(),
55
+ orderId: newOrderId.trim() || undefined,
56
+ });
57
+ },
58
+ onSuccess: () => {
59
+ showToast("Support ticket created.", "success");
60
+ setNewTicketOpen(false);
61
+ setNewSubject("");
62
+ setNewCategory("general");
63
+ setNewDescription("");
64
+ setNewOrderId("");
65
+ invalidate();
66
+ },
67
+ onError: (err) => {
68
+ showToast(err?.message ?? "Failed to create ticket.", "error");
69
+ },
70
+ });
71
+ const replyMutation = useMutation({
72
+ mutationFn: async () => {
73
+ await apiClient.post(SUPPORT_ENDPOINTS.TICKET_MESSAGES(selectedTicket.id), { body: replyBody.trim() });
74
+ },
75
+ onSuccess: () => {
76
+ showToast("Reply sent.", "success");
77
+ setReplyBody("");
78
+ invalidate();
79
+ },
80
+ onError: (err) => {
81
+ showToast(err?.message ?? "Failed to send reply.", "error");
82
+ },
83
+ });
84
+ return (_jsxs(_Fragment, { children: [_jsxs(Div, { className: "mx-auto max-w-2xl px-4 py-6", children: [_jsxs(Div, { className: "mb-4 flex items-center justify-between", children: [_jsx(Text, { className: "text-xl font-semibold text-zinc-900 dark:text-zinc-100", children: "Support Tickets" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => setNewTicketOpen(true), children: "New ticket" })] }), isLoading && (_jsx(Div, { className: "space-y-3", children: [1, 2, 3].map((i) => (_jsx("div", { className: "h-16 animate-pulse rounded-lg bg-zinc-100 dark:bg-zinc-800" }, i))) })), error && (_jsx(Div, { className: "rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: "Failed to load support tickets." })), !isLoading && tickets.length === 0 && (_jsx(Div, { className: "rounded-xl border border-zinc-200 bg-zinc-50 px-6 py-10 text-center dark:border-zinc-700 dark:bg-zinc-900/40", children: _jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: "You haven't opened any support tickets yet." }) })), _jsx("ul", { className: "space-y-3", children: tickets.map((ticket) => (_jsx("li", { children: _jsx("button", { type: "button", className: "w-full rounded-xl border border-zinc-200 bg-white p-4 text-left shadow-sm hover:border-primary-300 transition-colors dark:border-zinc-700 dark:bg-zinc-900", onClick: () => {
85
+ setSelectedTicket(ticket);
86
+ setDetailOpen(true);
87
+ }, children: _jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "font-medium text-zinc-900 dark:text-zinc-100 truncate", children: ticket.subject }), _jsxs("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: [ticket.category.replace(/_/g, " "), ticket.orderId ? ` · Order: ${ticket.orderId}` : ""] })] }), _jsx("span", { className: `shrink-0 inline-flex rounded-full px-2.5 py-0.5 text-xs font-medium ${STATUS_BADGE[ticket.status] ?? STATUS_BADGE.open}`, children: ticket.status.replace(/_/g, " ") })] }) }) }, ticket.id))) })] }), _jsx(SideDrawer, { isOpen: newTicketOpen, onClose: () => setNewTicketOpen(false), title: "Open a support ticket", children: _jsxs(Div, { className: "flex flex-col gap-4 p-4", children: [_jsx(Select, { label: "Category", options: CATEGORY_OPTIONS, value: newCategory, onValueChange: setNewCategory }), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Subject" }), _jsx("input", { type: "text", value: newSubject, onChange: (e) => setNewSubject(e.target.value), placeholder: "Brief description of the issue", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" })] }), newCategory === "order_issue" && (_jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Order ID" }), _jsx("input", { type: "text", value: newOrderId, onChange: (e) => setNewOrderId(e.target.value), placeholder: "e.g. order-3-20260508-a1b2c3", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" })] })), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Description" }), _jsx("textarea", { value: newDescription, onChange: (e) => setNewDescription(e.target.value), rows: 4, placeholder: "Describe the issue in detail\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" })] }), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: () => setNewTicketOpen(false), children: "Cancel" }), _jsx(Button, { type: "button", isLoading: createMutation.isPending, disabled: !newSubject.trim() || !newDescription.trim() || createMutation.isPending, onClick: () => createMutation.mutate(), children: "Submit ticket" })] })] }) }), _jsx(SideDrawer, { isOpen: detailOpen, onClose: () => setDetailOpen(false), title: selectedTicket?.subject ?? "Ticket", children: selectedTicket && (_jsxs(Div, { className: "flex flex-col gap-4 p-4", children: [_jsxs(Div, { className: "flex flex-wrap gap-2", children: [_jsx("span", { className: `inline-flex rounded-full px-2.5 py-1 text-xs font-medium ${STATUS_BADGE[selectedTicket.status] ?? STATUS_BADGE.open}`, children: selectedTicket.status.replace(/_/g, " ") }), _jsx("span", { className: "rounded-full bg-zinc-100 px-2.5 py-1 text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300", children: selectedTicket.category.replace(/_/g, " ") }), selectedTicket.orderId && (_jsxs("span", { className: "rounded-full bg-zinc-100 px-2.5 py-1 text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300", children: ["Order: ", selectedTicket.orderId] }))] }), selectedTicket.description && (_jsxs(Div, { className: "rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-900/40", children: [_jsx(Text, { className: "mb-1 text-xs font-semibold text-zinc-500 uppercase tracking-wide", children: "Your description" }), _jsx(Text, { className: "whitespace-pre-wrap text-sm text-zinc-700 dark:text-zinc-200", children: selectedTicket.description })] })), (selectedTicket.messages ?? []).length > 0 && (_jsxs(Div, { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold text-zinc-500 uppercase tracking-wide", children: "Messages" }), _jsx("div", { className: "space-y-2 max-h-72 overflow-y-auto", children: (selectedTicket.messages ?? []).map((msg, i) => (_jsxs("div", { className: `rounded-lg p-3 text-sm ${msg.authorRole === "user"
88
+ ? "bg-zinc-50 border border-zinc-200 dark:bg-zinc-900/40 dark:border-zinc-700"
89
+ : "bg-blue-50 border border-blue-200 dark:bg-blue-900/20 dark:border-blue-800"}`, children: [_jsxs("div", { className: "mb-1 flex items-center gap-2 text-xs text-zinc-400 dark:text-zinc-500", children: [_jsx("span", { className: "font-medium text-zinc-600 dark:text-zinc-300", children: ROLE_LABEL[msg.authorRole ?? "user"] ?? msg.authorRole }), msg.createdAt && (_jsx("span", { children: new Date(msg.createdAt).toLocaleString() }))] }), _jsx("p", { className: "whitespace-pre-wrap text-zinc-700 dark:text-zinc-200", children: msg.body })] }, msg.id ?? i))) })] })), selectedTicket.status !== "closed" && selectedTicket.status !== "resolved" && (_jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Reply" }), _jsx("textarea", { value: replyBody, onChange: (e) => setReplyBody(e.target.value), rows: 3, placeholder: "Add a message to your ticket\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: replyMutation.isPending, disabled: !replyBody.trim() || replyMutation.isPending, onClick: () => replyMutation.mutate(), children: "Send reply" })] }))] })) })] }));
90
+ }
@@ -38,3 +38,5 @@ export { UserOrderTrackView } from "./UserOrderTrackView";
38
38
  export type { UserOrderTrackViewProps, UserOrderTrackViewLabels, } from "./UserOrderTrackView";
39
39
  export { UserReturnsView } from "./UserReturnsView";
40
40
  export type { UserReturnsViewProps, UserReturnsViewLabels, } from "./UserReturnsView";
41
+ export { UserSupportView } from "./UserSupportView";
42
+ export type { UserSupportViewProps } from "./UserSupportView";
@@ -18,3 +18,4 @@ export { ChatWindow } from "./ChatWindow";
18
18
  export { BecomeSellerView } from "./BecomeSellerView";
19
19
  export { UserOrderTrackView } from "./UserOrderTrackView";
20
20
  export { UserReturnsView } from "./UserReturnsView";
21
+ export { UserSupportView } from "./UserSupportView";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "2.6.6",
3
+ "version": "2.6.7",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"