@oxyhq/services 0.0.59 → 0.0.61

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/README.md CHANGED
@@ -1,3 +1,107 @@
1
1
  # OxyServicesModule
2
2
 
3
- npm publish
3
+ ## Usage Instructions
4
+
5
+ To use the OxyServicesModule, follow these steps:
6
+
7
+ 1. Install the package:
8
+ ```bash
9
+ npm install @oxyhq/services
10
+ ```
11
+
12
+ 2. Import the necessary components and hooks in your project:
13
+ ```javascript
14
+ import { useOxySession, getUserById, SignInButton, AccountSwitcherModal, SessionOwnerButton } from '@oxyhq/services';
15
+ ```
16
+
17
+ 3. Use the components and hooks in your application. For example, to use the `SignInButton` component:
18
+ ```javascript
19
+ import React from 'react';
20
+ import { SignInButton } from '@oxyhq/services';
21
+
22
+ const App = () => {
23
+ return (
24
+ <div>
25
+ <SignInButton />
26
+ </div>
27
+ );
28
+ };
29
+
30
+ export default App;
31
+ ```
32
+
33
+ ## Examples
34
+
35
+ ### Example 1: Using `useOxySession` Hook
36
+
37
+ The `useOxySession` hook fetches session data and provides error handling and status management.
38
+
39
+ ```javascript
40
+ import React from 'react';
41
+ import { useOxySession } from '@oxyhq/services';
42
+
43
+ const SessionComponent = () => {
44
+ const { session, status, error } = useOxySession();
45
+
46
+ if (status === 'loading') {
47
+ return <div>Loading...</div>;
48
+ }
49
+
50
+ if (status === 'error') {
51
+ return <div>Error: {error}</div>;
52
+ }
53
+
54
+ return (
55
+ <div>
56
+ <h1>Welcome, {session.user.name}!</h1>
57
+ <p>Email: {session.user.email}</p>
58
+ </div>
59
+ );
60
+ };
61
+
62
+ export default SessionComponent;
63
+ ```
64
+
65
+ ### Example 2: Using `getUserById` Function
66
+
67
+ The `getUserById` function fetches user data by ID and handles possible errors.
68
+
69
+ ```javascript
70
+ import React, { useEffect, useState } from 'react';
71
+ import { getUserById } from '@oxyhq/services';
72
+
73
+ const UserComponent = ({ userId }) => {
74
+ const [user, setUser] = useState(null);
75
+ const [error, setError] = useState(null);
76
+
77
+ useEffect(() => {
78
+ const fetchUser = async () => {
79
+ try {
80
+ const fetchedUser = await getUserById(userId);
81
+ setUser(fetchedUser);
82
+ } catch (err) {
83
+ setError(err.message);
84
+ }
85
+ };
86
+
87
+ fetchUser();
88
+ }, [userId]);
89
+
90
+ if (error) {
91
+ return <div>Error: {error}</div>;
92
+ }
93
+
94
+ if (!user) {
95
+ return <div>Loading...</div>;
96
+ }
97
+
98
+ return (
99
+ <div>
100
+ <h1>{user.name}</h1>
101
+ <p>Email: {user.email}</p>
102
+ </div>
103
+ );
104
+ };
105
+
106
+ export default UserComponent;
107
+ ```
@@ -1 +1 @@
1
- {"version":3,"file":"AccountSwitcherModal.d.ts","sourceRoot":"","sources":["../../../src/components/auth/AccountSwitcherModal.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAyB/B,eAAO,MAAM,oBAAoB;aAEpB,MAAM,IAAI;2CAkErB,CAAC"}
1
+ {"version":3,"file":"AccountSwitcherModal.d.ts","sourceRoot":"","sources":["../../../src/components/auth/AccountSwitcherModal.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAyB/B,eAAO,MAAM,oBAAoB;aAEpB,MAAM,IAAI;2CA0ErB,CAAC"}
@@ -9,6 +9,7 @@ import { PiSignOutBold } from "react-icons/pi";
9
9
  import { AiOutlineClose } from "react-icons/ai";
10
10
  import { Avatar } from "../../features/profile";
11
11
  import getUserById from "../../hooks/getUserById";
12
+ import styles from "./styles/session-owner-modal.module.css";
12
13
  export const AccountSwitcherModal = forwardRef(({ onClose }, ref) => {
13
14
  const { session } = useOxySession();
14
15
  const [user, setUser] = useState(null);
@@ -23,11 +24,16 @@ export const AccountSwitcherModal = forwardRef(({ onClose }, ref) => {
23
24
  }, [session]);
24
25
  if (!session)
25
26
  return null;
27
+ const handleBackdropClick = (e) => {
28
+ if (e.currentTarget === e.target) {
29
+ onClose();
30
+ }
31
+ };
26
32
  return (React.createElement(motion.div, { initial: { opacity: 0, y: "100%" }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: "100%" }, transition: {
27
33
  ease: "easeInOut",
28
34
  duration: 0.2,
29
- }, className: "fixed m-auto top-0 bottom-0 left-0 right-0 w-fit h-fit z-10 flex flex-col items-center justify-center\n space-y-3 rounded-3xl bg-indigo-50 px-5 py-3 text-sm font-medium text-zinc-700\n shadow-md shadow-[#b4bebb]", role: "group" },
30
- React.createElement("button", { className: "absolute right-3 top-3 rounded-full bg-indigo-50 p-1 hover:bg-zinc-200" },
35
+ }, className: styles.container, role: "group", onClick: handleBackdropClick },
36
+ React.createElement("button", { className: "absolute right-3 top-3 rounded-full bg-indigo-50 p-1 hover:bg-zinc-200", onClick: onClose },
31
37
  React.createElement(AiOutlineClose, { className: "h-5 w-5 rounded-full stroke-2 text-zinc-700" })),
32
38
  React.createElement("p", null, session?.user?.email),
33
39
  React.createElement("div", { className: "h-20 w-20 rounded-full border" },
@@ -1 +1 @@
1
- {"version":3,"file":"getUserById.d.ts","sourceRoot":"","sources":["../../src/hooks/getUserById.ts"],"names":[],"mappings":"AAGA,UAAU,IAAI;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,QAAA,MAAM,WAAW,QAAe,MAAM,GAAG,MAAM,EAAE,WAAW,CAAC,MAAM,IAAI,CAAC,EAAE,kBAiBzE,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"getUserById.d.ts","sourceRoot":"","sources":["../../src/hooks/getUserById.ts"],"names":[],"mappings":"AAGA,UAAU,IAAI;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,QAAA,MAAM,WAAW,QAAe,MAAM,GAAG,MAAM,EAAE,WAAW,CAAC,MAAM,IAAI,CAAC,EAAE,kBA4BzE,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -3,6 +3,9 @@ import { OXY_AUTH_URL } from "../config";
3
3
  const getUserById = async (id, fields) => {
4
4
  try {
5
5
  const response = await axios.get(OXY_AUTH_URL + `/api/users/${id}`);
6
+ if (response.status !== 200) {
7
+ throw new Error(`Unexpected response status: ${response.status}`);
8
+ }
6
9
  const user = fields
7
10
  ? fields.reduce((obj, key) => ({ ...obj, [key]: response.data[key] }), {})
8
11
  : response.data;
@@ -10,7 +13,15 @@ const getUserById = async (id, fields) => {
10
13
  }
11
14
  catch (error) {
12
15
  if (axios.isAxiosError(error)) {
13
- throw new Error(error.message);
16
+ if (error.response) {
17
+ throw new Error(`Network error: ${error.message}, Status code: ${error.response.status}`);
18
+ }
19
+ else if (error.request) {
20
+ throw new Error("Network error: No response received from server.");
21
+ }
22
+ else {
23
+ throw new Error(`Network error: ${error.message}`);
24
+ }
14
25
  }
15
26
  else {
16
27
  throw new Error("An unknown error occurred while loading the user data.");
@@ -1,4 +1,3 @@
1
- type UserRole = "ADMIN" | "USER";
2
1
  interface SessionModel {
3
2
  user: {
4
3
  id: string;
@@ -9,9 +8,6 @@ interface SessionModel {
9
8
  verified: boolean;
10
9
  avatar: string;
11
10
  created_at: string;
12
- isTwoFactorEnabled: boolean;
13
- isOAuth: boolean;
14
- role: UserRole;
15
11
  };
16
12
  }
17
13
  type SessionState = {
@@ -1 +1 @@
1
- {"version":3,"file":"useOxySession.d.ts","sourceRoot":"","sources":["../../src/hooks/useOxySession.ts"],"names":[],"mappings":"AAOA,KAAK,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAEjC,UAAU,YAAY;IACpB,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,OAAO,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,OAAO,EAAE,OAAO,CAAC;QACjB,IAAI,EAAE,QAAQ,CAAC;KAChB,CAAC;CACH;AAED,KAAK,YAAY,GAAG;IAClB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,GAAG,CAAC;IACX,gBAAgB,EAAE,MAAM,IAAI,CAAC;CAC9B,CAAC;AAEF,eAAO,MAAM,eAAe,2EAuC1B,CAAC;AAEH,iBAAS,aAAa;;;;EAQrB;AAED,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"useOxySession.d.ts","sourceRoot":"","sources":["../../src/hooks/useOxySession.ts"],"names":[],"mappings":"AAOA,UAAU,YAAY;IACpB,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,OAAO,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,KAAK,YAAY,GAAG;IAClB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,GAAG,CAAC;IACX,gBAAgB,EAAE,MAAM,IAAI,CAAC;CAC9B,CAAC;AAEF,eAAO,MAAM,eAAe,2EA4D1B,CAAC;AAEH,iBAAS,aAAa;;;;EAQrB;AAED,eAAe,aAAa,CAAC"}
@@ -26,12 +26,39 @@ export const useSessionStore = create((set) => {
26
26
  // If the session ID was found in the URL parameters, set it in local storage
27
27
  await localforage.setItem("clientKey", clientKey);
28
28
  }
29
- const responseSessions = await axios.get(OXY_AUTH_URL + "/api/sessions/" + clientKey);
30
- const responseCurrentSession = await axios.get(OXY_AUTH_URL + "/api/session/" + responseSessions?.data.sessions[0].id);
31
- set({ session: responseCurrentSession.data, status: "success" });
29
+ const response = await axios.get(OXY_AUTH_URL + "/api/session/" + clientKey);
30
+ if (response.status !== 200) {
31
+ throw new Error(`Unexpected response status: ${response.status}`);
32
+ }
33
+ set({ session: response.data, status: "success" });
32
34
  }
33
35
  catch (error) {
34
- set({ error, status: "error" });
36
+ if (axios.isAxiosError(error)) {
37
+ if (error.response) {
38
+ set({
39
+ error: `Network error: ${error.message}, Status code: ${error.response.status}`,
40
+ status: "error",
41
+ });
42
+ }
43
+ else if (error.request) {
44
+ set({
45
+ error: "Network error: No response received from server.",
46
+ status: "error",
47
+ });
48
+ }
49
+ else {
50
+ set({
51
+ error: `Network error: ${error.message}`,
52
+ status: "error",
53
+ });
54
+ }
55
+ }
56
+ else {
57
+ set({
58
+ error: "An unknown error occurred while loading the session data.",
59
+ status: "error",
60
+ });
61
+ }
35
62
  }
36
63
  },
37
64
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/services",
3
- "version": "0.0.59",
3
+ "version": "0.0.61",
4
4
  "description": "",
5
5
  "homepage": "https://oxy.so/",
6
6
  "main": "./dist/index.js",
@@ -13,7 +13,7 @@
13
13
  "copy:css": "ncp temp dist",
14
14
  "build:ts": "tsc",
15
15
  "build": "npm run build:scss && npm run build:ts && npm run copy:css",
16
- "test": "echo \"Error: no test specified\" && exit 1",
16
+ "test": "jest",
17
17
  "lint": "eslint src/**/*.{ts,tsx}"
18
18
  },
19
19
  "keywords": [],
@@ -25,6 +25,7 @@
25
25
  "url": "https://github.com/OxyHQ/OxyServicesModule.git"
26
26
  },
27
27
  "devDependencies": {
28
+ "@types/jest": "^29.5.3",
28
29
  "@types/next-auth": "^3.15.0",
29
30
  "@types/node": "^20.12.12",
30
31
  "@types/react": "^18.3.3",
@@ -33,6 +34,7 @@
33
34
  "@typescript-eslint/parser": "^7.0.1",
34
35
  "autoprefixer": "^10.4.19",
35
36
  "eslint": "^8.56.0",
37
+ "jest": "^29.6.2",
36
38
  "ncp": "^2.0.0",
37
39
  "sass": "^1.77.2",
38
40
  "tailwindcss": "^3.4.3",
@@ -43,9 +45,9 @@
43
45
  "axios": "^1.6.8",
44
46
  "clsx": "^2.1.1",
45
47
  "dotenv": "^16.4.5",
46
- "framer-motion": "^11.3.19",
48
+ "framer-motion": "^11.2.9",
47
49
  "localforage": "^1.10.0",
48
- "next": "^14.2.5",
50
+ "next": "^15.0.3",
49
51
  "postcss": "^8.4.38",
50
52
  "react": "^18.3.1",
51
53
  "react-dom": "^18.3.1",
@@ -54,4 +56,4 @@
54
56
  "zod": "^3.23.8",
55
57
  "zustand": "^4.5.2"
56
58
  }
57
- }
59
+ }
@@ -1,7 +0,0 @@
1
- import React from "react";
2
- interface UserInfoProps {
3
- setDisplayUserInfo: React.Dispatch<React.SetStateAction<boolean>>;
4
- }
5
- declare function UserInfo({ setDisplayUserInfo }: UserInfoProps): React.JSX.Element;
6
- export default UserInfo;
7
- //# sourceMappingURL=AccountSwitcher.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AccountSwitcher.d.ts","sourceRoot":"","sources":["../../../src/components/auth/AccountSwitcher.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,UAAU,aAAa;IACrB,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;CACnE;AAED,iBAAS,QAAQ,CAAC,EAAE,kBAAkB,EAAE,EAAE,aAAa,qBA8CtD;AAED,eAAe,QAAQ,CAAC"}
@@ -1,27 +0,0 @@
1
- import React from "react";
2
- import Image from "next/image";
3
- import { signOut, useSession } from "next-auth/react";
4
- function UserInfo({ setDisplayUserInfo }) {
5
- const { data: session } = useSession();
6
- return (React.createElement("div", { className: "relative z-10 flex flex-col items-center justify-center\n space-y-3 rounded-2xl bg-darkC2 px-5 py-3 text-sm font-medium text-textC\n shadow-md shadow-[#b4bebb]" },
7
- React.createElement("button", { onClick: () => setDisplayUserInfo((prev) => false), className: "absolute right-3 top-3 rounded-full bg-darkC2 p-1 hover:bg-darkC" }),
8
- React.createElement("p", null, session?.user?.email),
9
- React.createElement("div", { className: "h-20 w-20 rounded-full border" },
10
- React.createElement(Image, { src: session?.user?.image, className: "h-full w-full rounded-full object-center", height: 500, width: 500, draggable: false, alt: "avatar" })),
11
- React.createElement("h2", { className: "tablet:text-2xl text-xl font-normal" },
12
- "Hi, ",
13
- session?.user?.name,
14
- "!"),
15
- React.createElement("button", { className: "rounded-full border border-black px-7 py-2 text-textC2 hover:bg-[#d3dfee]" }, "Manage your Google Account"),
16
- React.createElement("div", { className: "flex space-x-1" },
17
- React.createElement("button", { className: "tablet:w-44 flex w-36 items-center space-x-2 rounded-l-full bg-white py-3 pl-3 hover:bg-darkC" },
18
- React.createElement("span", null, "Add account")),
19
- React.createElement("button", { onClick: () => signOut(), className: "tablet:w-44 flex w-36 items-center space-x-2 rounded-r-full bg-white py-3 pl-3 hover:bg-darkC" },
20
- React.createElement("span", null, "Sign out"))),
21
- React.createElement("div", { className: "flex h-10 items-center space-x-2 text-xs" },
22
- React.createElement("span", null, "Privacy policy"),
23
- React.createElement("span", { className: "-mt-[3px]" }, " . "),
24
- " ",
25
- React.createElement("span", null, "Terms of service"))));
26
- }
27
- export default UserInfo;
@@ -1,3 +0,0 @@
1
- import SignInButton from "./SignInButton";
2
- export { SignInButton };
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,CAAC"}
@@ -1,2 +0,0 @@
1
- import SignInButton from "./SignInButton";
2
- export { SignInButton };
@@ -1,33 +0,0 @@
1
- .container {
2
- width: 100%;
3
- padding: 0.7em;
4
- border-radius: 100vmax;
5
- font-size: var(--fs-milli);
6
- font-weight: var(--fw-700);
7
- color: var(--clr-dark);
8
- background-color: var(--clr-light);
9
- border: 1px solid var(--clr-auth-border);
10
- display: flex;
11
- justify-content: center;
12
- align-items: center;
13
- cursor: pointer;
14
- transition: background 0.2s ease-in-out;
15
- }
16
- .container:hover {
17
- background-color: var(--clr-auth-button-hover);
18
- }
19
- .container:active {
20
- background-color: var(--clr-auth-button-active);
21
- }
22
- .container:focus-visible {
23
- outline: 2px solid var(--clr-light);
24
- background-color: var(--clr-auth-button-hover);
25
- }
26
- .container svg {
27
- width: var(--fs-h2);
28
- height: var(--fs-h2);
29
- fill: var(--clr-dark);
30
- margin-right: 0.5em;
31
- }
32
-
33
- /*# sourceMappingURL=sign-in-button.module.css.map */
@@ -1 +0,0 @@
1
- {"version":3,"sourceRoot":"","sources":["../../../src/components/auth/sign-in-button.module.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA","file":"sign-in-button.module.css"}