@pradip1995/segment-login-template 0.5.9 → 0.5.11

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pradip1995/segment-login-template",
3
- "version": "0.5.9",
3
+ "version": "0.5.11",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -22,10 +22,6 @@
22
22
  "./google-auth-section": "./src/google-auth-section.tsx",
23
23
  "./otp-input": "./src/otp-input.tsx"
24
24
  },
25
- "scripts": {
26
- "typecheck": "tsc --noEmit",
27
- "lint": "tsc --noEmit"
28
- },
29
25
  "peerDependencies": {
30
26
  "@pradip1995/commerce-auth": "^4.0.0",
31
27
  "@pradip1995/commerce-core": "^4.0.0",
@@ -45,5 +41,9 @@
45
41
  "@types/react": "^19",
46
42
  "react": "19.0.3",
47
43
  "typescript": "^5.7.2"
44
+ },
45
+ "scripts": {
46
+ "typecheck": "tsc --noEmit",
47
+ "lint": "tsc --noEmit"
48
48
  }
49
- }
49
+ }
@@ -1,16 +1,29 @@
1
1
  "use client"
2
2
 
3
- import { useMemo, useState } from "react"
3
+ import { useMemo, useState, type MouseEvent } from "react"
4
+ import { usePathname, useRouter } from "next/navigation"
4
5
  import type { HttpTypes } from "@medusajs/types"
6
+ import { addToCart } from "@pradip1995/commerce-core/client/actions/cart"
5
7
  import LocalizedLink from "@pradip1995/segment-primitives/localized-link"
6
8
  import { formatPrice } from "@pradip1995/segment-primitives/format-price"
7
9
  import { acct } from "./account-theme"
8
10
 
9
- function statusStyle(status?: string, fulfillment?: string) {
10
- const s = (fulfillment || status || "").toLowerCase()
11
- if (s.includes("cancel")) {
11
+ function isCancelled(order: HttpTypes.StoreOrder) {
12
+ const status = (order.status || "").toLowerCase()
13
+ return status === "canceled" || status === "cancelled"
14
+ }
15
+
16
+ function canReorder(order: HttpTypes.StoreOrder) {
17
+ if (isCancelled(order)) return false
18
+ const fulfillment = (order.fulfillment_status || "").toLowerCase()
19
+ return ["delivered", "partially_delivered"].includes(fulfillment)
20
+ }
21
+
22
+ function statusStyle(order: HttpTypes.StoreOrder) {
23
+ if (isCancelled(order)) {
12
24
  return "bg-[#FFF1F2] text-[#BE123C] border-[#BE123C]/20"
13
25
  }
26
+ const s = (order.fulfillment_status || order.status || "").toLowerCase()
14
27
  if (s === "delivered" || s === "complete" || s === "completed") {
15
28
  return "bg-[#E6F4F0] text-[#008A5D] border-[#008A5D]/20"
16
29
  }
@@ -23,16 +36,27 @@ function statusStyle(status?: string, fulfillment?: string) {
23
36
  return "bg-surface-muted text-muted border-cart-border"
24
37
  }
25
38
 
39
+ function statusLabel(order: HttpTypes.StoreOrder) {
40
+ if (isCancelled(order)) return "Cancelled"
41
+ return (order.fulfillment_status || order.status || "").replace(/_/g, " ")
42
+ }
43
+
26
44
  export default function AccountOrders({
27
45
  orders,
28
46
  }: {
29
47
  orders?: HttpTypes.StoreOrder[] | { orders?: HttpTypes.StoreOrder[] } | null
30
48
  }) {
31
49
  const [query, setQuery] = useState("")
50
+ const [reorderingId, setReorderingId] = useState<string | null>(null)
51
+ const router = useRouter()
52
+ const pathname = usePathname() || ""
53
+ const countryCode = pathname.split("/").filter(Boolean)[0] || "in"
32
54
 
33
55
  const safeOrders: HttpTypes.StoreOrder[] = useMemo(() => {
34
56
  if (Array.isArray(orders)) return orders
35
- if (Array.isArray((orders as any)?.orders)) return (orders as any).orders
57
+ if (Array.isArray((orders as { orders?: HttpTypes.StoreOrder[] })?.orders)) {
58
+ return (orders as { orders: HttpTypes.StoreOrder[] }).orders
59
+ }
36
60
  return []
37
61
  }, [orders])
38
62
 
@@ -41,16 +65,31 @@ export default function AccountOrders({
41
65
  if (!q) return safeOrders
42
66
  return safeOrders.filter((order) => {
43
67
  const id = String(order.display_id ?? order.id)
44
- const status = String(order.status ?? "").replace(/_/g, " ")
45
- const fulfillment = String(order.fulfillment_status ?? "").replace(/_/g, " ")
46
- return (
47
- id.includes(q) ||
48
- status.toLowerCase().includes(q) ||
49
- fulfillment.toLowerCase().includes(q)
50
- )
68
+ const status = statusLabel(order)
69
+ return id.includes(q) || status.toLowerCase().includes(q)
51
70
  })
52
71
  }, [safeOrders, query])
53
72
 
73
+ async function handleReorder(event: MouseEvent, order: HttpTypes.StoreOrder) {
74
+ event.preventDefault()
75
+ event.stopPropagation()
76
+ if (!canReorder(order) || reorderingId) return
77
+ setReorderingId(order.id)
78
+ try {
79
+ for (const item of order.items || []) {
80
+ if (!item.variant_id) continue
81
+ await addToCart({
82
+ variantId: item.variant_id,
83
+ quantity: item.quantity || 1,
84
+ countryCode,
85
+ })
86
+ }
87
+ router.push(`/${countryCode}/cart`)
88
+ } catch {
89
+ setReorderingId(null)
90
+ }
91
+ }
92
+
54
93
  return (
55
94
  <div className="space-y-6">
56
95
  <div className="pb-6 border-b border-cart-border">
@@ -84,28 +123,22 @@ export default function AccountOrders({
84
123
  ) : (
85
124
  <ul className="space-y-4">
86
125
  {filtered.map((order) => {
87
- const label = (
88
- order.fulfillment_status ||
89
- order.status ||
90
- ""
91
- ).replace(/_/g, " ")
126
+ const label = statusLabel(order)
127
+ const showReorder = canReorder(order)
92
128
  return (
93
129
  <li key={order.id}>
94
- <LocalizedLink
95
- href={`/orders/${order.id}`}
96
- className={`${acct.card} p-5 flex flex-wrap items-center justify-between gap-4 hover:border-brand-accent transition-colors block`}
130
+ <div
131
+ className={`${acct.card} p-5 flex flex-wrap items-center justify-between gap-4`}
97
132
  >
98
- <div className="space-y-2 min-w-0">
133
+ <LocalizedLink
134
+ href={`/orders/${order.id}`}
135
+ className="space-y-2 min-w-0 flex-1 hover:opacity-90 transition-opacity"
136
+ >
99
137
  <div className="flex flex-wrap items-center gap-2">
100
138
  <p className={`font-bold ${acct.heading}`}>
101
139
  Order #{order.display_id}
102
140
  </p>
103
- <span
104
- className={`${acct.badge} capitalize ${statusStyle(
105
- order.status,
106
- order.fulfillment_status
107
- )}`}
108
- >
141
+ <span className={`${acct.badge} capitalize ${statusStyle(order)}`}>
109
142
  {label}
110
143
  </span>
111
144
  </div>
@@ -118,11 +151,21 @@ export default function AccountOrders({
118
151
  {order.items?.length ?? 0} item
119
152
  {(order.items?.length ?? 0) === 1 ? "" : "s"}
120
153
  </p>
121
- </div>
122
- <p className={`text-base font-bold ${acct.accent} shrink-0`}>
123
- {formatPrice(order.total, order.currency_code)}
124
- </p>
125
- </LocalizedLink>
154
+ <p className={`text-base font-bold ${acct.accent}`}>
155
+ {formatPrice(order.total, order.currency_code)}
156
+ </p>
157
+ </LocalizedLink>
158
+ {showReorder ? (
159
+ <button
160
+ type="button"
161
+ className={acct.btnPrimary}
162
+ disabled={reorderingId === order.id}
163
+ onClick={(event) => handleReorder(event, order)}
164
+ >
165
+ {reorderingId === order.id ? "Adding…" : "Reorder"}
166
+ </button>
167
+ ) : null}
168
+ </div>
126
169
  </li>
127
170
  )
128
171
  })}
@@ -130,7 +130,9 @@ export function useAccountProfileLogic(
130
130
  }
131
131
 
132
132
  const res = await sendRegistrationOtp(customer.id, type)
133
- if (!res.success) throw new Error(res.error || "Failed to send code")
133
+ if (!res?.success) {
134
+ throw new Error(res?.error || "Failed to send code")
135
+ }
134
136
  setOtpToken(res.token ?? null)
135
137
  setOtp("")
136
138
  setOtpOpen(true)
@@ -157,7 +159,9 @@ export function useAccountProfileLogic(
157
159
  countryCode,
158
160
  skipRedirect: true,
159
161
  })
160
- if (!res.success) throw new Error(res.error || "Invalid code")
162
+ if (!res?.success) {
163
+ throw new Error(res?.error || "Invalid code")
164
+ }
161
165
  setOtpOpen(false)
162
166
  if (otpType === "phone_verification") {
163
167
  setPhoneVerified(true)
@@ -258,21 +258,42 @@ export async function sendRegistrationOtp(
258
258
  customerId: string,
259
259
  type: "email_verification" | "phone_verification"
260
260
  ) {
261
- const result = await sendCustomerOTP(customerId, type)
262
- if (result.success) {
263
- return result
264
- }
261
+ const response = await fetch(`${getBaseUrl()}/store/customers/otp/send`, {
262
+ method: "POST",
263
+ headers: getHeaders(),
264
+ body: JSON.stringify({
265
+ customer_id: customerId,
266
+ type,
267
+ }),
268
+ cache: "no-store",
269
+ })
265
270
 
266
- const message = result.error || "Failed to send verification code"
267
- if (message.includes("Channel configuration not found")) {
268
- return {
269
- success: false as const,
270
- error:
271
- "Verification email is not configured on the backend. Rebuild and restart the Medusa server with customer-registration OTP settings.",
271
+ if (!response.ok) {
272
+ const err = (await response.json().catch(() => ({}))) as {
273
+ message?: string
272
274
  }
275
+ const message = err.message || "Failed to send verification code"
276
+ if (message.includes("Channel configuration not found")) {
277
+ return {
278
+ success: false as const,
279
+ error:
280
+ "Verification is not configured on the backend. Rebuild and restart the Medusa server with customer-registration OTP settings.",
281
+ }
282
+ }
283
+ return { success: false as const, error: message }
273
284
  }
274
285
 
275
- return { success: false as const, error: message }
286
+ const data = (await response.json()) as {
287
+ token?: string
288
+ expires_at?: string
289
+ message?: string
290
+ }
291
+
292
+ return {
293
+ success: true as const,
294
+ token: data.token,
295
+ message: data.message || "OTP sent successfully",
296
+ }
276
297
  }
277
298
 
278
299
  export async function initiateGoogleAuth(countryCode?: string) {