@sanvika/auth 1.0.19 → 1.0.21

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.
Files changed (2) hide show
  1. package/dist/index.js +136 -325
  2. package/package.json +1 -2
package/dist/index.js CHANGED
@@ -1,105 +1,23 @@
1
1
  "use client";
2
2
 
3
3
  // SanvikaAuthProvider.jsx
4
- import {
5
- createContext,
6
- useContext,
7
- useEffect,
8
- useState,
9
- useCallback,
10
- useRef
11
- } from "react";
4
+ import { createContext, useContext, useEffect, useState } from "react";
12
5
 
13
6
  // constants.js
14
7
  var STORAGE_KEYS = {
15
8
  ACCESS_TOKEN: "sanvika_access_token",
16
- USER: "sanvika_user",
17
- STATE: "sanvika_oauth_state",
18
- // CSRF state for OAuth flow
19
- RETURN_PATH: "sanvika_return_path"
20
- // Page to redirect back after login
9
+ USER: "sanvika_user"
21
10
  };
22
11
  var DEFAULT_AVATAR_SVG = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Ccircle cx='20' cy='20' r='20' fill='%23e5e7eb'/%3E%3Ccircle cx='20' cy='15' r='7' fill='%23adb5bd'/%3E%3Cellipse cx='20' cy='35' rx='12' ry='8' fill='%23adb5bd'/%3E%3C/svg%3E`;
23
12
 
24
- // tokenRefreshUtils.js
25
- function decodeToken(token) {
26
- try {
27
- const base64Url = token.split(".")[1];
28
- const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
29
- const jsonPayload = decodeURIComponent(
30
- atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
31
- );
32
- return JSON.parse(jsonPayload);
33
- } catch {
34
- return null;
35
- }
36
- }
37
- async function ensureTokenFresh(token, thresholdSeconds = 300) {
38
- if (!token) return null;
39
- const decoded = decodeToken(token);
40
- if (!decoded || !decoded.exp) return token;
41
- const nowSeconds = Math.floor(Date.now() / 1e3);
42
- const secondsUntilExpiry = decoded.exp - nowSeconds;
43
- if (secondsUntilExpiry > thresholdSeconds) {
44
- return token;
45
- }
46
- try {
47
- const refreshRes = await fetch("/api/auth/refresh", {
48
- method: "POST",
49
- headers: { "Content-Type": "application/json" },
50
- credentials: "include"
51
- // Send httpOnly refresh token cookie (same domain)
52
- });
53
- if (!refreshRes.ok) {
54
- console.warn("[TokenRefresh] Refresh failed:", refreshRes.status);
55
- return token;
56
- }
57
- const data = await refreshRes.json();
58
- if (data.success && data.accessToken) {
59
- localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, data.accessToken);
60
- return data.accessToken;
61
- }
62
- } catch (err) {
63
- console.warn("[TokenRefresh] Error refreshing token:", err.message);
64
- }
65
- return token;
66
- }
67
- function scheduleTokenRefresh(token, onRefresh) {
68
- if (!token) return () => {
69
- };
70
- const decoded = decodeToken(token);
71
- if (!decoded || !decoded.exp) return () => {
72
- };
73
- const nowSeconds = Math.floor(Date.now() / 1e3);
74
- const secondsUntilExpiry = decoded.exp - nowSeconds;
75
- const refreshAt = Math.max(60, Math.floor(secondsUntilExpiry * 0.75));
76
- const timeoutId = setTimeout(async () => {
77
- try {
78
- const freshToken = await ensureTokenFresh(token, 0);
79
- if (freshToken !== token && onRefresh) {
80
- onRefresh(freshToken);
81
- }
82
- } catch (err) {
83
- console.warn("[TokenRefresh] Auto-refresh failed:", err.message);
84
- }
85
- }, refreshAt * 1e3);
86
- return () => clearTimeout(timeoutId);
87
- }
88
-
89
13
  // SanvikaAuthProvider.jsx
90
14
  import { jsx } from "react/jsx-runtime";
91
15
  var SA_URL = "https://accounts.sanvikaproduction.com";
92
16
  var SanvikaAuthContext = createContext(null);
93
- function SanvikaAuthProvider({
94
- children,
95
- clientId,
96
- redirectUri,
97
- dashboardPath = "/dashboard"
98
- }) {
17
+ function SanvikaAuthProvider({ children }) {
99
18
  const [user, setUser] = useState(null);
100
19
  const [accessToken, setToken] = useState(null);
101
20
  const [loading, setLoading] = useState(true);
102
- const refreshCleanupRef = useRef(null);
103
21
  useEffect(() => {
104
22
  try {
105
23
  const storedToken = localStorage.getItem(STORAGE_KEYS.ACCESS_TOKEN);
@@ -108,136 +26,69 @@ function SanvikaAuthProvider({
108
26
  setToken(storedToken);
109
27
  setUser(JSON.parse(storedUser));
110
28
  }
111
- } catch {
112
- localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN);
113
- localStorage.removeItem(STORAGE_KEYS.USER);
29
+ } catch (e) {
30
+ console.error("[SanvikaAuth] Failed to load from localStorage:", e);
114
31
  } finally {
115
32
  setLoading(false);
116
33
  }
117
34
  }, []);
118
- useEffect(() => {
119
- function onStorage(e) {
120
- if (e.key === STORAGE_KEYS.ACCESS_TOKEN && !e.newValue) {
121
- setUser(null);
122
- setToken(null);
123
- }
124
- if (e.key === STORAGE_KEYS.ACCESS_TOKEN && e.newValue) {
125
- try {
126
- const u = localStorage.getItem(STORAGE_KEYS.USER);
127
- if (u) setUser(JSON.parse(u));
128
- setToken(e.newValue);
129
- } catch {
130
- }
131
- }
132
- }
133
- window.addEventListener("storage", onStorage);
134
- return () => window.removeEventListener("storage", onStorage);
135
- }, []);
136
- useEffect(() => {
137
- if (!accessToken) {
138
- if (refreshCleanupRef.current) {
139
- refreshCleanupRef.current();
140
- refreshCleanupRef.current = null;
141
- }
142
- return;
143
- }
144
- const cleanup = scheduleTokenRefresh(accessToken, (freshToken) => {
145
- localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, freshToken);
146
- setToken(freshToken);
147
- window.dispatchEvent(
148
- new StorageEvent("storage", {
149
- key: STORAGE_KEYS.ACCESS_TOKEN,
150
- newValue: freshToken,
151
- oldValue: accessToken
152
- })
153
- );
35
+ const login = async ({ mobile, password, deviceId, deviceName }) => {
36
+ const response = await fetch(`${SA_URL}/api/auth/login`, {
37
+ method: "POST",
38
+ headers: { "Content-Type": "application/json" },
39
+ credentials: "include",
40
+ body: JSON.stringify({
41
+ mobile,
42
+ password,
43
+ deviceId: deviceId || crypto.randomUUID(),
44
+ deviceName: deviceName || "Browser"
45
+ })
154
46
  });
155
- refreshCleanupRef.current = cleanup;
156
- return () => {
157
- if (cleanup) cleanup();
158
- };
159
- }, [accessToken]);
160
- const login = useCallback((token, userData) => {
161
- localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, token);
162
- localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(userData));
163
- setToken(token);
164
- setUser(userData);
165
- }, []);
166
- const logout = useCallback(async () => {
167
- const token = localStorage.getItem(STORAGE_KEYS.ACCESS_TOKEN);
168
- if (token) {
169
- fetch(`${SA_URL}/api/auth/logout`, {
47
+ const data = await response.json();
48
+ if (!data.success) throw new Error(data.error);
49
+ localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, data.accessToken);
50
+ localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(data.user));
51
+ setToken(data.accessToken);
52
+ setUser(data.user);
53
+ return data;
54
+ };
55
+ const logout = async (logoutAll = false) => {
56
+ try {
57
+ await fetch(`${SA_URL}/api/auth/logout`, {
170
58
  method: "POST",
171
59
  headers: {
172
60
  "Content-Type": "application/json",
173
- Authorization: `Bearer ${token}`
61
+ Authorization: `Bearer ${accessToken}`
174
62
  },
175
- body: JSON.stringify({ logout_all: false })
176
- }).catch(() => {
63
+ credentials: "include",
64
+ body: JSON.stringify({ logout_all: logoutAll })
177
65
  });
66
+ } catch (e) {
67
+ console.error("[SanvikaAuth] Logout API error:", e);
68
+ } finally {
69
+ localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN);
70
+ localStorage.removeItem(STORAGE_KEYS.USER);
71
+ setToken(null);
72
+ setUser(null);
178
73
  }
179
- localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN);
180
- localStorage.removeItem(STORAGE_KEYS.USER);
181
- setUser(null);
182
- setToken(null);
183
- }, []);
184
- const updateUser = useCallback((partial) => {
185
- setUser((prev) => {
186
- if (!prev) return prev;
187
- const merged = { ...prev, ...partial };
188
- localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(merged));
189
- return merged;
190
- });
191
- }, []);
192
- const redirectToLogin = useCallback(
193
- (returnPath) => {
194
- if (returnPath) {
195
- localStorage.setItem(STORAGE_KEYS.RETURN_PATH, returnPath);
196
- }
197
- const state = Math.random().toString(36).slice(2);
198
- localStorage.setItem(STORAGE_KEYS.STATE, state);
199
- const url = new URL(`${SA_URL}/authorize`);
200
- url.searchParams.set("client_id", clientId);
201
- url.searchParams.set("redirect_uri", redirectUri);
202
- url.searchParams.set("response_type", "code");
203
- url.searchParams.set("state", state);
204
- const appTheme = localStorage.getItem("theme");
205
- if (appTheme === "light" || appTheme === "dark") {
206
- url.searchParams.set("theme", appTheme);
207
- }
208
- window.location.href = url.toString();
209
- },
210
- [clientId, redirectUri]
211
- );
212
- return /* @__PURE__ */ jsx(
213
- SanvikaAuthContext.Provider,
214
- {
215
- value: {
216
- user,
217
- accessToken,
218
- loading,
219
- isLoggedIn: !!user,
220
- login,
221
- logout,
222
- updateUser,
223
- redirectToLogin,
224
- clientId,
225
- redirectUri,
226
- dashboardPath
227
- },
228
- children
229
- }
230
- );
74
+ };
75
+ const value = {
76
+ user,
77
+ accessToken,
78
+ loading,
79
+ isAuthenticated: !!user,
80
+ login,
81
+ logout
82
+ };
83
+ return /* @__PURE__ */ jsx(SanvikaAuthContext.Provider, { value, children });
231
84
  }
232
85
  function useSanvikaAuth() {
233
86
  const ctx = useContext(SanvikaAuthContext);
234
- if (!ctx)
235
- throw new Error("useSanvikaAuth must be used inside <SanvikaAuthProvider>");
236
87
  return ctx;
237
88
  }
238
89
 
239
90
  // SanvikaAccountButton.jsx
240
- import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
91
+ import { useEffect as useEffect2, useRef, useState as useState2, Component } from "react";
241
92
 
242
93
  // #style-inject:#style-inject
243
94
  function styleInject(css, { insertAt } = {}) {
@@ -262,10 +113,26 @@ function styleInject(css, { insertAt } = {}) {
262
113
  }
263
114
 
264
115
  // SanvikaAccountButton.css
265
- styleInject("@keyframes snvk-shimmer {\n 0% {\n background-position: 200% 0;\n }\n 100% {\n background-position: -200% 0;\n }\n}\n.snvk-skeleton {\n width: clamp(72px, 20vw, 96px);\n height: clamp(34px, 8vw, 40px);\n border-radius: 8px;\n background:\n linear-gradient(\n 90deg,\n var(--skeleton-base-color, #d0d0d0) 25%,\n var(--skeleton-highlight-color, #f0f0f0) 50%,\n var(--skeleton-base-color, #d0d0d0) 75%);\n background-size: 200% 100%;\n animation: snvk-shimmer 1.4s infinite;\n display: inline-block;\n}\n.snvk-wrapper {\n position: relative;\n display: inline-block;\n}\n.snvk-guestBtn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: clamp(6px, 2vw, 8px) clamp(10px, 3vw, 16px);\n min-height: 44px;\n min-width: 44px;\n background: var(--sanvika-brand-color, #4f46e5);\n color: #ffffff;\n border: none;\n border-radius: 8px;\n font-size: clamp(13px, 3vw, 14px);\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.3s ease, color 0.3s ease;\n white-space: nowrap;\n}\n.snvk-guestBtn:hover {\n background: var(--sanvika-brand-hover, #4338ca);\n}\n.snvk-iconWrap {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n}\n.snvk-profileBtn {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n padding: 5px 10px 5px 5px;\n min-height: 44px;\n background: var(--muted-bg, #f5f5f5);\n border: 1px solid var(--border-color-light, #e5e7eb);\n border-radius: 99px;\n font-size: clamp(13px, 3vw, 14px);\n font-weight: 600;\n cursor: pointer;\n color: var(--text-color, #1a1a1a);\n transition:\n background-color 0.3s ease,\n border-color 0.3s ease,\n color 0.3s ease;\n white-space: nowrap;\n outline: none;\n}\n.snvk-profileBtn:hover {\n background: var(--hover-color, #ebebeb);\n border-color: var(--border-color-dark, #d1d5db);\n}\n.snvk-profileBtn:focus-visible {\n outline: 2px solid var(--sanvika-brand-color, #4f46e5);\n outline-offset: 2px;\n}\n.snvk-avatar {\n width: 28px;\n height: 28px;\n border-radius: 50%;\n object-fit: cover;\n border: 2px solid var(--sanvika-brand-color, #4f46e5);\n flex-shrink: 0;\n}\n.snvk-textWrap {\n display: inline;\n}\n.snvk-hideTextOnMobile {\n display: none;\n}\n@media (min-width: 500px) {\n .snvk-hideTextOnMobile {\n display: inline;\n }\n}\n.snvk-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n min-width: clamp(200px, 60vw, 240px);\n background: var(--card-bg, #f3f3f3);\n border: 1px solid var(--border-color-light, #cccccc);\n border-radius: 12px;\n box-shadow: 0 8px 24px var(--shadow-color, rgba(0, 0, 0, 0.2));\n z-index: 9999;\n overflow: hidden;\n transition: background-color 0.3s ease, border-color 0.3s ease;\n}\n.snvk-dropdownHeader {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 14px 16px;\n background: var(--section-bg, #ebebeb);\n transition: background-color 0.3s ease;\n}\n.snvk-dropdownAvatar {\n width: 38px;\n height: 38px;\n border-radius: 50%;\n object-fit: cover;\n border: 2px solid var(--sanvika-brand-color, #4f46e5);\n flex-shrink: 0;\n}\n.snvk-dropdownName {\n font-size: clamp(13px, 3vw, 14px);\n font-weight: 800;\n color: var(--text-color, #000000);\n line-height: 1.3;\n transition: color 0.3s ease;\n}\n.snvk-dropdownMobile {\n font-size: clamp(11px, 2.5vw, 12px);\n color: var(--secondary-text-color, #333333);\n margin-top: 2px;\n transition: color 0.3s ease;\n font-weight: 700;\n}\n.snvk-divider {\n height: 1px;\n background: var(--muted-border, #d8d8d8);\n transition: background-color 0.3s ease;\n}\n.snvk-dropdownItem {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 11px 16px;\n min-height: 44px;\n background: none;\n border: none;\n font-size: clamp(13px, 3vw, 14px);\n color: var(--text-color, #000000);\n font-weight: 600;\n cursor: pointer;\n text-decoration: none;\n text-align: left;\n transition: background-color 0.3s ease, color 0.3s ease;\n}\n.snvk-dropdownItem:hover {\n background: var(--hover-color, #e8e8e8);\n}\n.snvk-logoutItem {\n color: var(--error-color, #c0392b);\n}\n.snvk-logoutItem:hover {\n background: var(--error-bg, #fff5f5);\n}\n.snvk-deleteItem {\n color: var(--warning-color, #d97706);\n}\n.snvk-deleteItem:hover {\n background: var(--warning-bg, #fffbf0);\n}\n");
116
+ styleInject("@keyframes snvk-shimmer {\n 0% {\n background-position: 200% 0;\n }\n 100% {\n background-position: -200% 0;\n }\n}\n.snvk-skeleton {\n width: clamp(72px, 20vw, 96px);\n height: clamp(34px, 8vw, 40px);\n border-radius: 8px;\n background:\n linear-gradient(\n 90deg,\n var(--skeleton-base-color, #d0d0d0) 25%,\n var(--skeleton-highlight-color, #f0f0f0) 50%,\n var(--skeleton-base-color, #d0d0d0) 75%);\n background-size: 200% 100%;\n animation: snvk-shimmer 1.4s infinite;\n display: inline-block;\n}\n.snvk-wrapper {\n position: relative;\n display: inline-block;\n}\n.snvk-guestBtn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: clamp(6px, 2vw, 8px) clamp(10px, 3vw, 16px);\n min-height: 44px;\n min-width: 44px;\n background: var(--sanvika-brand-color, #4f46e5);\n color: #ffffff;\n border: none;\n border-radius: 8px;\n font-size: clamp(13px, 3vw, 14px);\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.3s ease, color 0.3s ease;\n white-space: nowrap;\n}\n.snvk-guestBtn:hover {\n background: var(--sanvika-brand-hover, #4338ca);\n}\n.snvk-iconWrap {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n}\n.snvk-profileBtn {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n padding: 5px 10px 5px 5px;\n min-height: 44px;\n background: var(--muted-bg, #f5f5f5);\n border: 1px solid var(--border-color-light, #e5e7eb);\n border-radius: 99px;\n font-size: clamp(13px, 3vw, 14px);\n font-weight: 600;\n cursor: pointer;\n color: var(--text-color, #1a1a1a);\n transition:\n background-color 0.3s ease,\n border-color 0.3s ease,\n color 0.3s ease;\n white-space: nowrap;\n outline: none;\n}\n.snvk-profileBtn:hover {\n background: var(--hover-color, #ebebeb);\n border-color: var(--border-color-dark, #d1d5db);\n}\n.snvk-profileBtn:focus-visible {\n outline: 2px solid var(--sanvika-brand-color, #4f46e5);\n outline-offset: 2px;\n}\n.snvk-avatar {\n width: 28px;\n height: 28px;\n border-radius: 50%;\n object-fit: cover;\n border: 2px solid var(--sanvika-brand-color, #4f46e5);\n flex-shrink: 0;\n}\n.snvk-textWrap {\n display: inline;\n}\n.snvk-hideTextOnMobile {\n display: none;\n}\n@media (min-width: 500px) {\n .snvk-hideTextOnMobile {\n display: inline;\n }\n}\n.snvk-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n min-width: clamp(200px, 60vw, 240px);\n background: var(--card-bg, #f3f3f3);\n border: 1px solid var(--border-color-light, #cccccc);\n border-radius: 12px;\n box-shadow: 0 8px 24px var(--shadow-color, rgba(0, 0, 0, 0.2));\n z-index: 9999;\n overflow: hidden;\n transition: background-color 0.3s ease, border-color 0.3s ease;\n}\n.snvk-dropdownHeader {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 14px 16px;\n background: var(--section-bg, #ebebeb);\n transition: background-color 0.3s ease;\n}\n.snvk-dropdownAvatar {\n width: 38px;\n height: 38px;\n border-radius: 50%;\n object-fit: cover;\n border: 2px solid var(--sanvika-brand-color, #4f46e5);\n flex-shrink: 0;\n}\n.snvk-dropdownName {\n font-size: clamp(13px, 3vw, 14px);\n font-weight: 800;\n color: var(--text-color, #000000);\n line-height: 1.3;\n transition: color 0.3s ease;\n}\n.snvk-dropdownMobile {\n font-size: clamp(11px, 2.5vw, 12px);\n color: var(--secondary-text-color, #333333);\n margin-top: 2px;\n transition: color 0.3s ease;\n font-weight: 700;\n}\n.snvk-divider {\n height: 1px;\n background: var(--muted-border, #d8d8d8);\n transition: background-color 0.3s ease;\n}\n.snvk-dropdownItem {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 11px 16px;\n min-height: 44px;\n background: none;\n border: none;\n font-size: clamp(13px, 3vw, 14px);\n color: var(--text-color, #000000);\n font-weight: 600;\n cursor: pointer;\n text-decoration: none;\n text-align: left;\n transition: background-color 0.3s ease, color 0.3s ease;\n}\n.snvk-dropdownItem:hover {\n background: var(--hover-color, #e8e8e8);\n}\n");
266
117
 
267
118
  // SanvikaAccountButton.jsx
268
119
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
120
+ var SanvikaAccountButtonErrorBoundary = class extends Component {
121
+ constructor(props) {
122
+ super(props);
123
+ this.state = { hasError: false };
124
+ }
125
+ static getDerivedStateFromError(error) {
126
+ return { hasError: true };
127
+ }
128
+ static displayName = "SanvikaAccountButtonErrorBoundary";
129
+ render() {
130
+ if (this.state.hasError) {
131
+ return /* @__PURE__ */ jsx2("div", { className: "snvk-skeleton", "aria-hidden": "true" });
132
+ }
133
+ return this.props.children;
134
+ }
135
+ };
269
136
  function UserIcon({ size = 18 }) {
270
137
  return /* @__PURE__ */ jsxs(
271
138
  "svg",
@@ -324,89 +191,20 @@ function DashboardIcon() {
324
191
  }
325
192
  );
326
193
  }
327
- function LogoutIcon() {
328
- return /* @__PURE__ */ jsxs(
329
- "svg",
330
- {
331
- width: "15",
332
- height: "15",
333
- viewBox: "0 0 24 24",
334
- fill: "none",
335
- stroke: "currentColor",
336
- strokeWidth: "2",
337
- strokeLinecap: "round",
338
- children: [
339
- /* @__PURE__ */ jsx2("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
340
- /* @__PURE__ */ jsx2("polyline", { points: "16 17 21 12 16 7" }),
341
- /* @__PURE__ */ jsx2("line", { x1: "21", y1: "12", x2: "9", y2: "12" })
342
- ]
343
- }
344
- );
345
- }
346
- function DeleteIcon() {
347
- return /* @__PURE__ */ jsxs(
348
- "svg",
349
- {
350
- width: "15",
351
- height: "15",
352
- viewBox: "0 0 24 24",
353
- fill: "none",
354
- stroke: "currentColor",
355
- strokeWidth: "2",
356
- strokeLinecap: "round",
357
- children: [
358
- /* @__PURE__ */ jsx2("polyline", { points: "3 6 5 6 21 6" }),
359
- /* @__PURE__ */ jsx2("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }),
360
- /* @__PURE__ */ jsx2("line", { x1: "10", y1: "11", x2: "10", y2: "17" }),
361
- /* @__PURE__ */ jsx2("line", { x1: "14", y1: "11", x2: "14", y2: "17" })
362
- ]
363
- }
364
- );
365
- }
366
- function SanvikaAccountButton({
367
- text = "Account",
368
- hideTextOnMobile = false,
194
+ function SanvikaAccountButtonContent({
195
+ text,
196
+ hideTextOnMobile,
369
197
  onLoginClick,
370
198
  onProfileClick,
371
- onboardingPath = null,
372
- className = ""
199
+ onboardingPath,
200
+ className
373
201
  }) {
374
- var _a;
375
- const {
376
- user,
377
- loading,
378
- isLoggedIn,
379
- logout,
380
- redirectToLogin,
381
- dashboardPath,
382
- accessToken,
383
- updateUser
384
- } = useSanvikaAuth();
202
+ var _a, _b;
203
+ const auth = useSanvikaAuth();
385
204
  const [dropdownOpen, setDropdownOpen] = useState2(false);
386
205
  const [imgError, setImgError] = useState2(false);
387
- const [prevImage, setPrevImage] = useState2(user == null ? void 0 : user.image);
388
- const wrapperRef = useRef2(null);
389
- if ((user == null ? void 0 : user.image) !== prevImage) {
390
- setPrevImage(user == null ? void 0 : user.image);
391
- setImgError(false);
392
- }
393
- useEffect2(() => {
394
- if (!user) return;
395
- let disposed = false;
396
- const handleProfileUpdated = async () => {
397
- if (disposed) return;
398
- try {
399
- const stored = localStorage.getItem("sanvika_user");
400
- if (stored) updateUser(JSON.parse(stored));
401
- } catch {
402
- }
403
- };
404
- window.addEventListener("profileUpdated", handleProfileUpdated);
405
- return () => {
406
- disposed = true;
407
- window.removeEventListener("profileUpdated", handleProfileUpdated);
408
- };
409
- }, [user, updateUser]);
206
+ const [prevImage, setPrevImage] = useState2((_a = auth == null ? void 0 : auth.user) == null ? void 0 : _a.image);
207
+ const wrapperRef = useRef(null);
410
208
  useEffect2(() => {
411
209
  if (!dropdownOpen) return;
412
210
  function handleOutside(e) {
@@ -417,10 +215,38 @@ function SanvikaAccountButton({
417
215
  document.addEventListener("mousedown", handleOutside);
418
216
  return () => document.removeEventListener("mousedown", handleOutside);
419
217
  }, [dropdownOpen]);
420
- if (loading) {
421
- return /* @__PURE__ */ jsx2("div", { className: "snvk-skeleton", "aria-hidden": "true" });
218
+ if (!auth) {
219
+ return /* @__PURE__ */ jsxs(
220
+ "button",
221
+ {
222
+ className: `snvk-guestBtn ${className}`,
223
+ onClick: () => {
224
+ if (onLoginClick) {
225
+ onLoginClick();
226
+ } else {
227
+ window.location.href = "https://accounts.sanvikaproduction.com";
228
+ }
229
+ },
230
+ "aria-label": "Login or Sign Up",
231
+ children: [
232
+ /* @__PURE__ */ jsx2("span", { className: "snvk-iconWrap", children: /* @__PURE__ */ jsx2(UserIcon, { size: 18 }) }),
233
+ /* @__PURE__ */ jsx2(
234
+ "span",
235
+ {
236
+ className: hideTextOnMobile ? "snvk-hideTextOnMobile" : "snvk-textWrap",
237
+ children: text
238
+ }
239
+ )
240
+ ]
241
+ }
242
+ );
422
243
  }
423
- if (!isLoggedIn) {
244
+ const { user, loading, isAuthenticated, logout } = auth;
245
+ if ((user == null ? void 0 : user.image) !== prevImage) {
246
+ setPrevImage(user == null ? void 0 : user.image);
247
+ setImgError(false);
248
+ }
249
+ if (!isAuthenticated || loading) {
424
250
  return /* @__PURE__ */ jsxs(
425
251
  "button",
426
252
  {
@@ -429,7 +255,7 @@ function SanvikaAccountButton({
429
255
  if (onLoginClick) {
430
256
  onLoginClick();
431
257
  } else {
432
- redirectToLogin(window.location.pathname);
258
+ window.location.href = "https://accounts.sanvikaproduction.com";
433
259
  }
434
260
  },
435
261
  "aria-label": "Login or Sign Up",
@@ -446,15 +272,15 @@ function SanvikaAccountButton({
446
272
  }
447
273
  );
448
274
  }
449
- const displayName = user.firstName || ((_a = user.mobile) == null ? void 0 : _a.slice(-4)) || "Me";
275
+ const displayName = user.firstName || ((_b = user.mobile) == null ? void 0 : _b.slice(-4)) || "Me";
450
276
  const imageSrc = !imgError && user.image ? user.image : DEFAULT_AVATAR_SVG;
451
277
  const handleProfileClick = () => {
452
278
  if (onProfileClick) return onProfileClick();
453
- if (onboardingPath && user.status === "onboarding") {
454
- window.location.href = onboardingPath;
455
- } else {
456
- window.location.href = dashboardPath;
457
- }
279
+ window.location.href = "https://accounts.sanvikaproduction.com/dashboard";
280
+ };
281
+ const handleLogout = async () => {
282
+ await logout(false);
283
+ setDropdownOpen(false);
458
284
  };
459
285
  return /* @__PURE__ */ jsxs("div", { ref: wrapperRef, className: `snvk-wrapper ${className}`, children: [
460
286
  /* @__PURE__ */ jsxs(
@@ -509,20 +335,7 @@ function SanvikaAccountButton({
509
335
  /* @__PURE__ */ jsxs(
510
336
  "a",
511
337
  {
512
- href: `${dashboardPath}/user-profile`,
513
- className: "snvk-dropdownItem",
514
- role: "menuitem",
515
- onClick: () => setDropdownOpen(false),
516
- children: [
517
- /* @__PURE__ */ jsx2(UserIcon, { size: 15 }),
518
- /* @__PURE__ */ jsx2("span", { children: "View Profile" })
519
- ]
520
- }
521
- ),
522
- /* @__PURE__ */ jsxs(
523
- "a",
524
- {
525
- href: onboardingPath && user.status === "onboarding" ? onboardingPath : dashboardPath,
338
+ href: "https://accounts.sanvikaproduction.com/dashboard",
526
339
  className: "snvk-dropdownItem",
527
340
  role: "menuitem",
528
341
  onClick: () => setDropdownOpen(false),
@@ -532,33 +345,31 @@ function SanvikaAccountButton({
532
345
  ]
533
346
  }
534
347
  ),
535
- /* @__PURE__ */ jsxs(
536
- "a",
537
- {
538
- href: `https://accounts.sanvikaproduction.com/account/delete?token=${encodeURIComponent(
539
- accessToken || ""
540
- )}&origin=${encodeURIComponent(window.location.origin)}`,
541
- className: "snvk-dropdownItem snvk-deleteItem",
542
- role: "menuitem",
543
- onClick: () => setDropdownOpen(false),
544
- children: [
545
- /* @__PURE__ */ jsx2(DeleteIcon, {}),
546
- /* @__PURE__ */ jsx2("span", { children: "Delete Account" })
547
- ]
548
- }
549
- ),
348
+ /* @__PURE__ */ jsx2("div", { className: "snvk-divider" }),
550
349
  /* @__PURE__ */ jsxs(
551
350
  "button",
552
351
  {
553
- className: "snvk-dropdownItem snvk-logoutItem",
352
+ className: "snvk-dropdownItem",
554
353
  role: "menuitem",
555
- onClick: async () => {
556
- setDropdownOpen(false);
557
- await logout();
558
- window.location.href = "/";
559
- },
354
+ onClick: handleLogout,
560
355
  children: [
561
- /* @__PURE__ */ jsx2(LogoutIcon, {}),
356
+ /* @__PURE__ */ jsxs(
357
+ "svg",
358
+ {
359
+ width: "15",
360
+ height: "15",
361
+ viewBox: "0 0 24 24",
362
+ fill: "none",
363
+ stroke: "currentColor",
364
+ strokeWidth: "2",
365
+ strokeLinecap: "round",
366
+ children: [
367
+ /* @__PURE__ */ jsx2("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
368
+ /* @__PURE__ */ jsx2("polyline", { points: "16 17 21 12 16 7" }),
369
+ /* @__PURE__ */ jsx2("line", { x1: "21", y1: "12", x2: "9", y2: "12" })
370
+ ]
371
+ }
372
+ ),
562
373
  /* @__PURE__ */ jsx2("span", { children: "Logout" })
563
374
  ]
564
375
  }
@@ -566,14 +377,14 @@ function SanvikaAccountButton({
566
377
  ] })
567
378
  ] });
568
379
  }
380
+ function SanvikaAccountButton(props) {
381
+ return /* @__PURE__ */ jsx2(SanvikaAccountButtonErrorBoundary, { children: /* @__PURE__ */ jsx2(SanvikaAccountButtonContent, { ...props }) });
382
+ }
569
383
  export {
570
384
  DEFAULT_AVATAR_SVG,
571
385
  STORAGE_KEYS,
572
386
  SanvikaAccountButton,
573
387
  SanvikaAuthContext,
574
388
  SanvikaAuthProvider,
575
- decodeToken,
576
- ensureTokenFresh,
577
- scheduleTokenRefresh,
578
389
  useSanvikaAuth
579
390
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanvika/auth",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "Sanvika Auth SDK — React components and hooks for Sanvika SSO integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,7 +29,6 @@
29
29
  "sanvika",
30
30
  "auth",
31
31
  "sso",
32
- "oauth",
33
32
  "react",
34
33
  "nextjs"
35
34
  ],