@caseparts-org/casecore 0.0.4 → 0.0.6
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 +60 -60
- package/dist/environment.d.ts +3 -0
- package/dist/environment.js +2 -0
- package/dist/src/App.d.ts +3 -0
- package/dist/src/App.js +46 -0
- package/dist/{authentication → src/lib/authentication}/AuthContext.d.ts +2 -2
- package/dist/{authentication → src/lib/authentication}/AuthContext.test.js +25 -25
- package/dist/{authentication → src/lib/authentication}/AuthTypes.d.ts +36 -36
- package/dist/src/lib/hooks/useMicroFlex.d.ts +27 -0
- package/dist/src/lib/hooks/useMicroFlex.js +138 -0
- package/dist/{index.d.ts → src/lib/index.d.ts} +2 -0
- package/dist/{index.js → src/lib/index.js} +1 -0
- package/dist/{utils → src/lib/utils}/ClaimsUtils.js +27 -27
- package/dist/src/local/impersonate/Impersonate.d.ts +1 -0
- package/dist/src/local/impersonate/Impersonate.js +62 -0
- package/dist/src/local/layout/Layout.d.ts +4 -0
- package/dist/src/local/layout/Layout.js +33 -0
- package/dist/src/local/login/Login.d.ts +1 -0
- package/dist/src/local/login/Login.js +53 -0
- package/dist/src/local/microflex/MicroFlex.d.ts +1 -0
- package/dist/src/local/microflex/MicroFlex.js +47 -0
- package/dist/src/local/util/AppSettings.d.ts +109 -0
- package/dist/src/local/util/AppSettings.js +138 -0
- package/dist/src/main.d.ts +1 -0
- package/dist/src/main.js +6 -0
- package/package.json +54 -53
- package/dist/authentication/AuthContext.helpers.d.ts +0 -0
- package/dist/authentication/AuthContext.helpers.js +0 -1
- /package/dist/{authentication → src/lib/authentication}/AuthContext.js +0 -0
- /package/dist/{authentication → src/lib/authentication}/AuthContext.test.d.ts +0 -0
- /package/dist/{authentication → src/lib/authentication}/AuthTypes.js +0 -0
- /package/dist/{hooks → src/lib/hooks}/useLocalStorage.d.ts +0 -0
- /package/dist/{hooks → src/lib/hooks}/useLocalStorage.js +0 -0
- /package/dist/{test → src/lib/test}/setup.d.ts +0 -0
- /package/dist/{test → src/lib/test}/setup.js +0 -0
- /package/dist/{utils → src/lib/utils}/ClaimsUtils.d.ts +0 -0
- /package/dist/{utils → src/lib/utils}/SessionUtils.d.ts +0 -0
- /package/dist/{utils → src/lib/utils}/SessionUtils.js +0 -0
package/README.md
CHANGED
@@ -1,60 +1,60 @@
|
|
1
|
-
# CaseCore React Component & Logic Library
|
2
|
-
|
3
|
-
This project is a reusable logic and component library for React and Next.js applications, designed to house all core business logic for our front-end apps, decoupled from any specific UI. While it currently focuses on authentication, claims, and session management, it is intended to grow into a central place for all shared business logic, utilities, and core functionality needed across our projects.
|
4
|
-
|
5
|
-
## Project Structure
|
6
|
-
|
7
|
-
- **/lib/**
|
8
|
-
- Contains all code intended for export as part of the library.
|
9
|
-
- Includes authentication context/provider, hooks, utility functions, and type definitions.
|
10
|
-
- The main entry point for consumers is `lib/index.ts`.
|
11
|
-
- **/src/App.tsx**
|
12
|
-
- Local demo/test app for development, not included in the library build.
|
13
|
-
- Useful for running and testing components locally with `npm run dev`.
|
14
|
-
- **/public/**
|
15
|
-
- Static assets for local development/demo.
|
16
|
-
- **/index.html**
|
17
|
-
- Entry point for local development with Vite.
|
18
|
-
|
19
|
-
## Key Features
|
20
|
-
|
21
|
-
- **Authentication Context (`AuthProvider`)**
|
22
|
-
- Provides authentication state, login/logout/impersonate methods, and claims to your app via React context.
|
23
|
-
- Handles JWT parsing, localStorage/sessionStorage, and API integration.
|
24
|
-
- **Claims & Session Utilities**
|
25
|
-
- `buildClaimsFromPayload`: Normalizes JWT payloads into a consistent claims object.
|
26
|
-
- `getSessionId`: Manages session IDs in sessionStorage.
|
27
|
-
- `useLocalStorage`: React hook for persistent state with automatic key prefixing.
|
28
|
-
- **TypeScript-first**
|
29
|
-
- All types and interfaces are defined in `lib/authentication/AuthTypes.ts` for strong typing and IDE support.
|
30
|
-
- **Testing**
|
31
|
-
- Tests are colocated with their modules (e.g., `AuthContext.test.tsx`).
|
32
|
-
- Mocks for fetch and JWT decoding are used to simulate authentication flows.
|
33
|
-
|
34
|
-
## Usage
|
35
|
-
|
36
|
-
- **As a library:**
|
37
|
-
- Import from `lib/index.ts` in your consuming app:
|
38
|
-
```ts
|
39
|
-
import { AuthProvider, useAuthContext } from 'casecore/lib';
|
40
|
-
```
|
41
|
-
- **For local development:**
|
42
|
-
- Run `npm run dev` to start the Vite dev server and use the demo app in `src/App.tsx`.
|
43
|
-
- **To run tests locally:**
|
44
|
-
- Run `npm test` or `npx vitest run` to execute all tests.
|
45
|
-
- For watch mode (auto-re-run on file changes), use:
|
46
|
-
```sh
|
47
|
-
npx vitest
|
48
|
-
```
|
49
|
-
- Test files are colocated with their modules (e.g., `AuthContext.test.tsx`).
|
50
|
-
|
51
|
-
## Contributing
|
52
|
-
|
53
|
-
- Add new core logic, hooks, or components to `/lib`.
|
54
|
-
- Add or update exports in `lib/index.ts`.
|
55
|
-
- Use `/src` and `/public` for local-only demo/testing code.
|
56
|
-
- Write and run tests for all exported logic.
|
57
|
-
|
58
|
-
## License
|
59
|
-
|
60
|
-
MIT
|
1
|
+
# CaseCore React Component & Logic Library
|
2
|
+
|
3
|
+
This project is a reusable logic and component library for React and Next.js applications, designed to house all core business logic for our front-end apps, decoupled from any specific UI. While it currently focuses on authentication, claims, and session management, it is intended to grow into a central place for all shared business logic, utilities, and core functionality needed across our projects.
|
4
|
+
|
5
|
+
## Project Structure
|
6
|
+
|
7
|
+
- **/lib/**
|
8
|
+
- Contains all code intended for export as part of the library.
|
9
|
+
- Includes authentication context/provider, hooks, utility functions, and type definitions.
|
10
|
+
- The main entry point for consumers is `lib/index.ts`.
|
11
|
+
- **/src/App.tsx**
|
12
|
+
- Local demo/test app for development, not included in the library build.
|
13
|
+
- Useful for running and testing components locally with `npm run dev`.
|
14
|
+
- **/public/**
|
15
|
+
- Static assets for local development/demo.
|
16
|
+
- **/index.html**
|
17
|
+
- Entry point for local development with Vite.
|
18
|
+
|
19
|
+
## Key Features
|
20
|
+
|
21
|
+
- **Authentication Context (`AuthProvider`)**
|
22
|
+
- Provides authentication state, login/logout/impersonate methods, and claims to your app via React context.
|
23
|
+
- Handles JWT parsing, localStorage/sessionStorage, and API integration.
|
24
|
+
- **Claims & Session Utilities**
|
25
|
+
- `buildClaimsFromPayload`: Normalizes JWT payloads into a consistent claims object.
|
26
|
+
- `getSessionId`: Manages session IDs in sessionStorage.
|
27
|
+
- `useLocalStorage`: React hook for persistent state with automatic key prefixing.
|
28
|
+
- **TypeScript-first**
|
29
|
+
- All types and interfaces are defined in `lib/authentication/AuthTypes.ts` for strong typing and IDE support.
|
30
|
+
- **Testing**
|
31
|
+
- Tests are colocated with their modules (e.g., `AuthContext.test.tsx`).
|
32
|
+
- Mocks for fetch and JWT decoding are used to simulate authentication flows.
|
33
|
+
|
34
|
+
## Usage
|
35
|
+
|
36
|
+
- **As a library:**
|
37
|
+
- Import from `lib/index.ts` in your consuming app:
|
38
|
+
```ts
|
39
|
+
import { AuthProvider, useAuthContext } from 'casecore/lib';
|
40
|
+
```
|
41
|
+
- **For local development:**
|
42
|
+
- Run `npm run dev` to start the Vite dev server and use the demo app in `src/App.tsx`.
|
43
|
+
- **To run tests locally:**
|
44
|
+
- Run `npm test` or `npx vitest run` to execute all tests.
|
45
|
+
- For watch mode (auto-re-run on file changes), use:
|
46
|
+
```sh
|
47
|
+
npx vitest
|
48
|
+
```
|
49
|
+
- Test files are colocated with their modules (e.g., `AuthContext.test.tsx`).
|
50
|
+
|
51
|
+
## Contributing
|
52
|
+
|
53
|
+
- Add new core logic, hooks, or components to `/lib`.
|
54
|
+
- Add or update exports in `lib/index.ts`.
|
55
|
+
- Use `/src` and `/public` for local-only demo/testing code.
|
56
|
+
- Write and run tests for all exported logic.
|
57
|
+
|
58
|
+
## License
|
59
|
+
|
60
|
+
MIT
|
package/dist/src/App.js
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
+
import './App.css';
|
3
|
+
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
4
|
+
import AuthProvider from './lib/authentication/AuthContext';
|
5
|
+
import Login from './local/login/Login';
|
6
|
+
import Impersonate from './local/impersonate/Impersonate';
|
7
|
+
import AppSettings from './local/util/AppSettings';
|
8
|
+
import Layout from './local/layout/Layout';
|
9
|
+
import MicroFlex from './local/microflex/MicroFlex';
|
10
|
+
function App() {
|
11
|
+
// You can define your AuthProvider props here or import them
|
12
|
+
const urls = {
|
13
|
+
Login: AppSettings.Authentication.Login,
|
14
|
+
Logout: AppSettings.Authentication.Logout,
|
15
|
+
Impersonate: (email) => AppSettings.Authentication.Impersonate(email),
|
16
|
+
};
|
17
|
+
const apiKey = import.meta.env.VITE_APIKEY;
|
18
|
+
const sessionClientId = 'casecore';
|
19
|
+
return (_jsx(AuthProvider, { urls: urls, apiKey: apiKey, sessionClientId: sessionClientId, children: _jsx(BrowserRouter, { children: _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(Home, {}) }), _jsx(Route, { path: "/login", element: _jsx(Login, {}) }), _jsx(Route, { path: "/impersonate", element: _jsx(Impersonate, {}) }), _jsx(Route, { path: "/microflex", element: _jsx(MicroFlex, {}) })] }) }) }));
|
20
|
+
}
|
21
|
+
function Home() {
|
22
|
+
return (_jsx(Layout, { children: _jsx("h1", { children: "Home" }) }));
|
23
|
+
}
|
24
|
+
// const urls = {
|
25
|
+
// Login: '/api/login',
|
26
|
+
// Logout: '/api/logout',
|
27
|
+
// Impersonate: (email: string) => `/api/impersonate/${email}`,
|
28
|
+
// };
|
29
|
+
// const apiKey = 'test-api-key';
|
30
|
+
// const sessionClientId = 'test-session-id';
|
31
|
+
// function TestConsumer() {
|
32
|
+
// const ctx = useAuthContext();
|
33
|
+
// if (!ctx) return <div>no context</div>;
|
34
|
+
// return (
|
35
|
+
// <div>
|
36
|
+
// <div data-testid="initialized">{ctx.initialized}</div>
|
37
|
+
// <div data-testid="email">{ctx.email}</div>
|
38
|
+
// <div data-testid="userType">{ctx.userType}</div>
|
39
|
+
// <div data-testid="claims">{JSON.stringify(ctx.claims)}</div>
|
40
|
+
// <button onClick={() => ctx.login('test@example.com', 'pw')}>Login</button>
|
41
|
+
// <button onClick={() => ctx.logout()}>Logout</button>
|
42
|
+
// <button onClick={() => ctx.impersonate('imp@example.com')}>Impersonate</button>
|
43
|
+
// </div>
|
44
|
+
// );
|
45
|
+
// }
|
46
|
+
export default App;
|
@@ -3,7 +3,7 @@ import { Claims } from "./AuthTypes";
|
|
3
3
|
export type AuthUrls = {
|
4
4
|
Login: string;
|
5
5
|
Logout: string;
|
6
|
-
Impersonate: (_email: string) => string;
|
6
|
+
Impersonate: (_email: string | null) => string;
|
7
7
|
};
|
8
8
|
export type AuthProviderProps = {
|
9
9
|
children: ReactNode;
|
@@ -19,7 +19,7 @@ export type AuthContextType = {
|
|
19
19
|
userType: string;
|
20
20
|
login: (_email?: string, _password?: string) => Promise<unknown>;
|
21
21
|
logout: () => Promise<boolean>;
|
22
|
-
impersonate: (_email: string) => Promise<string | null>;
|
22
|
+
impersonate: (_email: string | null) => Promise<string | null>;
|
23
23
|
fetch: (_url: string, _options?: RequestInit) => Promise<Response | null>;
|
24
24
|
hasRight: (_right: string) => boolean;
|
25
25
|
token: string | null;
|
@@ -5,33 +5,33 @@ import { useAuthContext, default as AuthProvider } from './AuthContext';
|
|
5
5
|
import { buildClaimsFromPayload } from '../utils/ClaimsUtils';
|
6
6
|
// Dummy data for API responses
|
7
7
|
const dummyEmployee = {
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
UserId: 'emp1',
|
9
|
+
UserName: 'employee@example.com',
|
10
|
+
NickName: 'Emp',
|
11
|
+
Branch: 'HQ',
|
12
|
+
Rights: 'VIEW,EDIT',
|
13
13
|
};
|
14
14
|
const dummyCustomer = {
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
15
|
+
UserName: 'customer@example.com',
|
16
|
+
UserId: 'cust1',
|
17
|
+
CustKey: 123,
|
18
|
+
CustId: 'C123',
|
19
|
+
CustName: 'Test Customer',
|
20
|
+
CustClass: 'Dealer',
|
21
|
+
FirstName: 'John',
|
22
|
+
LastName: 'Doe',
|
23
|
+
Address: '123 Main St',
|
24
|
+
City: 'Metropolis',
|
25
|
+
State: 'NY',
|
26
|
+
ZipCode: '10001',
|
27
|
+
Phone: '555-1234',
|
28
|
+
Hq: 'HQ',
|
29
|
+
HqCustKey: '456',
|
30
|
+
PaymentTerms: 'Net30',
|
31
|
+
LastLoginDate: '2024-01-01',
|
32
|
+
Rights: 'BUY,SELL',
|
33
|
+
PendingInvites: ['invite1'],
|
34
|
+
PrimaryAdmin: 'admin1',
|
35
35
|
};
|
36
36
|
const dummyJwtPayload = {
|
37
37
|
Time: 'now',
|
@@ -1,45 +1,45 @@
|
|
1
1
|
export type EmployeeResponse = {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
UserId: string;
|
3
|
+
UserName: string;
|
4
|
+
NickName: string;
|
5
|
+
Branch: string;
|
6
|
+
Rights: string;
|
7
7
|
};
|
8
8
|
export type CustomerResponse = {
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
9
|
+
UserName: string;
|
10
|
+
UserId: string;
|
11
|
+
CustKey: number;
|
12
|
+
CustId: string;
|
13
|
+
CustName: string;
|
14
|
+
CustClass: string;
|
15
|
+
FirstName: string;
|
16
|
+
LastName: string;
|
17
|
+
Address: string;
|
18
|
+
City: string;
|
19
|
+
State: string;
|
20
|
+
ZipCode: string;
|
21
|
+
Phone: string;
|
22
|
+
HQ: string;
|
23
|
+
HQCustKey: string;
|
24
|
+
PaymentTerms: string;
|
25
|
+
LastLoginDate: string | null;
|
26
|
+
Rights: string;
|
27
|
+
PendingInvites: Invite[];
|
28
|
+
PrimaryAdmin: string;
|
29
29
|
};
|
30
30
|
export type InviteType = "NewAcctPrimary" | "Standard" | "Primary" | "LinkedStandard" | "LinkedPrimary";
|
31
31
|
export type Invite = {
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
32
|
+
Id: string;
|
33
|
+
SenderEmail: string;
|
34
|
+
CustKey: number;
|
35
|
+
CustId: string;
|
36
|
+
CustName: string;
|
37
|
+
TargetEmail: string;
|
38
|
+
TargetUserId: string;
|
39
|
+
InviteType: InviteType | string;
|
40
|
+
Rights: string;
|
41
|
+
Status: string;
|
42
|
+
CreatedDate: string;
|
43
43
|
};
|
44
44
|
export type EmployeeClaims = {
|
45
45
|
UserId: string;
|
@@ -0,0 +1,27 @@
|
|
1
|
+
export type MicroFlexFieldSelectors = {
|
2
|
+
number: string;
|
3
|
+
securityCode: string;
|
4
|
+
};
|
5
|
+
export type MicroFlexOptions = {
|
6
|
+
initializeForm: boolean;
|
7
|
+
apiURL: string;
|
8
|
+
fetchFn: (_url: string, _options?: RequestInit) => Promise<Response>;
|
9
|
+
fieldSelectors?: MicroFlexFieldSelectors;
|
10
|
+
styles?: Record<string, any>;
|
11
|
+
scriptUrl?: string;
|
12
|
+
setupInterval?: number;
|
13
|
+
onError?: (_error: unknown) => void;
|
14
|
+
};
|
15
|
+
export type MicroFlexReturn = {
|
16
|
+
validate: () => Promise<string | undefined>;
|
17
|
+
cardToken: string | null;
|
18
|
+
cardError: string | null;
|
19
|
+
setupError: unknown;
|
20
|
+
autoCompleteData: unknown;
|
21
|
+
};
|
22
|
+
declare global {
|
23
|
+
interface Window {
|
24
|
+
Flex?: any;
|
25
|
+
}
|
26
|
+
}
|
27
|
+
export default function useMicroFlex({ initializeForm, apiURL, fetchFn, fieldSelectors, styles, scriptUrl, setupInterval, onError, }: MicroFlexOptions): MicroFlexReturn;
|
@@ -0,0 +1,138 @@
|
|
1
|
+
import { useState, useEffect, useRef } from "react";
|
2
|
+
const defaultStyles = {
|
3
|
+
input: {
|
4
|
+
"font-size": "1rem",
|
5
|
+
"font-family": "Roboto, helvetica, tahoma, calibri, sans-serif",
|
6
|
+
color: "#555",
|
7
|
+
"line-height": "1rem",
|
8
|
+
},
|
9
|
+
":focus": { color: "blue" },
|
10
|
+
":disabled": { cursor: "not-allowed" },
|
11
|
+
valid: { color: "#3C763D" },
|
12
|
+
invalid: { color: "#A94442" },
|
13
|
+
};
|
14
|
+
const defaultFieldSelectors = { number: "#number", securityCode: "#securityCode" };
|
15
|
+
const defaultScriptUrl = "https://flex.cybersource.com/cybersource/assets/microform/0.11/flex-microform.min.js";
|
16
|
+
export default function useMicroFlex({ initializeForm, apiURL, fetchFn, fieldSelectors = defaultFieldSelectors, styles = defaultStyles, scriptUrl = defaultScriptUrl, setupInterval = 15 * 60 * 1000, onError, }) {
|
17
|
+
const [error, setError] = useState(null);
|
18
|
+
const [autoCompleteData, setAutoCompleteData] = useState(null);
|
19
|
+
const [setupError, setSetupError] = useState(null);
|
20
|
+
const [token, setToken] = useState(null);
|
21
|
+
const formRef = useRef(null);
|
22
|
+
useEffect(() => {
|
23
|
+
if (!initializeForm)
|
24
|
+
return;
|
25
|
+
const scriptLoaded = !!window.Flex;
|
26
|
+
if (!scriptLoaded) {
|
27
|
+
const script = document.createElement("script");
|
28
|
+
script.src = scriptUrl;
|
29
|
+
script.async = true;
|
30
|
+
script.onload = () => {
|
31
|
+
setupMicroform();
|
32
|
+
};
|
33
|
+
document.body.appendChild(script);
|
34
|
+
}
|
35
|
+
try {
|
36
|
+
setupMicroform();
|
37
|
+
}
|
38
|
+
catch {
|
39
|
+
// Script might not be loaded yet
|
40
|
+
}
|
41
|
+
const timerId = setInterval(() => setupMicroform(), setupInterval);
|
42
|
+
return () => {
|
43
|
+
clearInterval(timerId);
|
44
|
+
};
|
45
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
46
|
+
}, [initializeForm, apiURL, scriptUrl]);
|
47
|
+
async function setupMicroform() {
|
48
|
+
if (!window.Flex)
|
49
|
+
return;
|
50
|
+
setToken(null);
|
51
|
+
setError(null);
|
52
|
+
let jwt;
|
53
|
+
try {
|
54
|
+
jwt = await fetchFn(apiURL, { method: "GET" })
|
55
|
+
.then((resp) => resp.text())
|
56
|
+
.catch((err) => {
|
57
|
+
setSetupError(err);
|
58
|
+
if (onError)
|
59
|
+
onError(err);
|
60
|
+
return undefined;
|
61
|
+
});
|
62
|
+
}
|
63
|
+
catch (err) {
|
64
|
+
setSetupError(err);
|
65
|
+
if (onError)
|
66
|
+
onError(err);
|
67
|
+
return;
|
68
|
+
}
|
69
|
+
if (!jwt)
|
70
|
+
return;
|
71
|
+
try {
|
72
|
+
const flex = new window.Flex(jwt);
|
73
|
+
const microform = flex.microform({ styles });
|
74
|
+
formRef.current = microform;
|
75
|
+
const number = microform.createField("number");
|
76
|
+
const securityCode = microform.createField("securityCode");
|
77
|
+
number.on("autocomplete", function (data) {
|
78
|
+
setAutoCompleteData(data);
|
79
|
+
setToken(null);
|
80
|
+
});
|
81
|
+
securityCode.on("autocomplete", function (data) {
|
82
|
+
setAutoCompleteData(data);
|
83
|
+
setToken(null);
|
84
|
+
});
|
85
|
+
number.on("change", () => {
|
86
|
+
setToken(null);
|
87
|
+
setError(null);
|
88
|
+
});
|
89
|
+
securityCode.on("change", () => {
|
90
|
+
setToken(null);
|
91
|
+
setError(null);
|
92
|
+
});
|
93
|
+
number.load(fieldSelectors.number);
|
94
|
+
securityCode.load(fieldSelectors.securityCode);
|
95
|
+
}
|
96
|
+
catch (error) {
|
97
|
+
setSetupError(error);
|
98
|
+
if (onError)
|
99
|
+
onError(error);
|
100
|
+
}
|
101
|
+
}
|
102
|
+
function validate() {
|
103
|
+
return new Promise((resolve, reject) => {
|
104
|
+
if (!formRef.current) {
|
105
|
+
const err = new Error("Form reference is not available.");
|
106
|
+
if (onError)
|
107
|
+
onError(err);
|
108
|
+
return reject(err);
|
109
|
+
}
|
110
|
+
formRef.current.createToken({}, function (error, token) {
|
111
|
+
let errorMessage = error?.message;
|
112
|
+
if (error && error.status === 429) {
|
113
|
+
errorMessage = "Credit card could not be validated. Please refresh and try again.";
|
114
|
+
}
|
115
|
+
if (error && error.status === 400) {
|
116
|
+
errorMessage = "Invalid credit card. Please double-check details.";
|
117
|
+
}
|
118
|
+
setError(errorMessage);
|
119
|
+
setToken(token);
|
120
|
+
if (error) {
|
121
|
+
if (onError)
|
122
|
+
onError(error);
|
123
|
+
reject(new Error(errorMessage));
|
124
|
+
}
|
125
|
+
else {
|
126
|
+
resolve(token);
|
127
|
+
}
|
128
|
+
});
|
129
|
+
});
|
130
|
+
}
|
131
|
+
return {
|
132
|
+
validate,
|
133
|
+
cardToken: token,
|
134
|
+
cardError: error,
|
135
|
+
setupError,
|
136
|
+
autoCompleteData,
|
137
|
+
};
|
138
|
+
}
|
@@ -4,3 +4,5 @@ export type { EmployeeResponse, CustomerResponse, InviteType, Invite, EmployeeCl
|
|
4
4
|
export { default as useLocalStorage } from './hooks/useLocalStorage';
|
5
5
|
export { getSessionId } from './utils/SessionUtils';
|
6
6
|
export { buildClaimsFromPayload } from './utils/ClaimsUtils';
|
7
|
+
export type { MicroFlexFieldSelectors, MicroFlexOptions, MicroFlexReturn } from './hooks/useMicroFlex';
|
8
|
+
export { default as useMicroFlex } from './hooks/useMicroFlex';
|
@@ -4,3 +4,4 @@ export { default as AuthProvider, useAuthContext } from './authentication/AuthCo
|
|
4
4
|
export { default as useLocalStorage } from './hooks/useLocalStorage';
|
5
5
|
export { getSessionId } from './utils/SessionUtils';
|
6
6
|
export { buildClaimsFromPayload } from './utils/ClaimsUtils';
|
7
|
+
export { default as useMicroFlex } from './hooks/useMicroFlex';
|
@@ -26,12 +26,12 @@ export function buildClaimsFromPayload(payload) {
|
|
26
26
|
let employeeClaims = null;
|
27
27
|
if (employeeRaw && typeof employeeRaw === "object") {
|
28
28
|
employeeClaims = {
|
29
|
-
UserId: employeeRaw.
|
30
|
-
UserName: employeeRaw.
|
31
|
-
NickName: employeeRaw.
|
32
|
-
Branch: employeeRaw.
|
33
|
-
Rights: typeof employeeRaw.
|
34
|
-
? employeeRaw.
|
29
|
+
UserId: employeeRaw.UserId,
|
30
|
+
UserName: employeeRaw.UserName,
|
31
|
+
NickName: employeeRaw.NickName,
|
32
|
+
Branch: employeeRaw.Branch ?? null,
|
33
|
+
Rights: typeof employeeRaw.Rights === "string"
|
34
|
+
? employeeRaw.Rights.split(",").map((s) => s.trim())
|
35
35
|
: [],
|
36
36
|
};
|
37
37
|
}
|
@@ -39,28 +39,28 @@ export function buildClaimsFromPayload(payload) {
|
|
39
39
|
let customerClaims = null;
|
40
40
|
if (customerRaw && typeof customerRaw === "object") {
|
41
41
|
customerClaims = {
|
42
|
-
UserName: customerRaw.
|
43
|
-
UserId: customerRaw.
|
44
|
-
CustKey: customerRaw.
|
45
|
-
CustId: customerRaw.
|
46
|
-
CustName: customerRaw.
|
47
|
-
CustClass: customerRaw.
|
48
|
-
FirstName: customerRaw.
|
49
|
-
LastName: customerRaw.
|
50
|
-
Address: customerRaw.
|
51
|
-
City: customerRaw.
|
52
|
-
State: customerRaw.
|
53
|
-
ZipCode: customerRaw.
|
54
|
-
Phone: customerRaw.
|
55
|
-
HQ: customerRaw.
|
56
|
-
HQCustKey: typeof customerRaw.
|
57
|
-
PaymentTerms: customerRaw.
|
58
|
-
LastLoginDate: customerRaw.
|
59
|
-
Rights: typeof customerRaw.
|
60
|
-
? customerRaw.
|
42
|
+
UserName: customerRaw.UserName,
|
43
|
+
UserId: customerRaw.UserId,
|
44
|
+
CustKey: customerRaw.CustKey,
|
45
|
+
CustId: customerRaw.CustId,
|
46
|
+
CustName: customerRaw.CustName,
|
47
|
+
CustClass: customerRaw.CustClass,
|
48
|
+
FirstName: customerRaw.FirstName,
|
49
|
+
LastName: customerRaw.LastName,
|
50
|
+
Address: customerRaw.Address,
|
51
|
+
City: customerRaw.City,
|
52
|
+
State: customerRaw.State,
|
53
|
+
ZipCode: customerRaw.ZipCode,
|
54
|
+
Phone: customerRaw.Phone,
|
55
|
+
HQ: customerRaw.HQ ?? null,
|
56
|
+
HQCustKey: typeof customerRaw.HQCustKey === "string" ? parseInt(customerRaw.HQCustKey, 10) : customerRaw.HQCustKey,
|
57
|
+
PaymentTerms: customerRaw.PaymentTerms,
|
58
|
+
LastLoginDate: customerRaw.LastLoginDate ?? "",
|
59
|
+
Rights: typeof customerRaw.Rights === "string"
|
60
|
+
? customerRaw.Rights.split(",").map((s) => s.trim())
|
61
61
|
: [],
|
62
|
-
PendingInvites: customerRaw.
|
63
|
-
PrimaryAdmin: customerRaw.
|
62
|
+
PendingInvites: customerRaw.PendingInvites ?? [],
|
63
|
+
PrimaryAdmin: customerRaw.PrimaryAdmin ?? null,
|
64
64
|
};
|
65
65
|
}
|
66
66
|
return {
|
@@ -0,0 +1 @@
|
|
1
|
+
export default function Impersonate(): import("react/jsx-runtime").JSX.Element;
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
+
import { useState } from 'react';
|
3
|
+
import Layout from '../layout/Layout';
|
4
|
+
import { useAuthContext } from '../../lib/authentication/AuthContext';
|
5
|
+
import AppSettings from '../util/AppSettings';
|
6
|
+
export default function Impersonate() {
|
7
|
+
const { fetch, impersonate } = useAuthContext() || {};
|
8
|
+
const [search, setSearch] = useState('');
|
9
|
+
const [results, setResults] = useState([]);
|
10
|
+
const [loading, setLoading] = useState(false);
|
11
|
+
const [error, setError] = useState(null);
|
12
|
+
const handleSearch = async () => {
|
13
|
+
if (!fetch || !search)
|
14
|
+
return;
|
15
|
+
setLoading(true);
|
16
|
+
setError(null);
|
17
|
+
try {
|
18
|
+
const url = AppSettings.Authentication.ImpersonateSearch + encodeURIComponent(search);
|
19
|
+
const res = await fetch(url);
|
20
|
+
if (!res)
|
21
|
+
throw new Error('No response');
|
22
|
+
const data = await res.json();
|
23
|
+
setResults(Array.isArray(data) ? data : (data.results || []));
|
24
|
+
}
|
25
|
+
catch (e) {
|
26
|
+
setError(e.message || 'Search failed');
|
27
|
+
setResults([]);
|
28
|
+
}
|
29
|
+
finally {
|
30
|
+
setLoading(false);
|
31
|
+
}
|
32
|
+
};
|
33
|
+
const handleKeyDown = (e) => {
|
34
|
+
if (e.key === 'Enter')
|
35
|
+
handleSearch();
|
36
|
+
};
|
37
|
+
const handleSelect = async (email) => {
|
38
|
+
if (!impersonate)
|
39
|
+
return;
|
40
|
+
try {
|
41
|
+
await impersonate(email);
|
42
|
+
// Optionally, redirect or show a message
|
43
|
+
}
|
44
|
+
catch {
|
45
|
+
setError('Impersonation failed');
|
46
|
+
}
|
47
|
+
};
|
48
|
+
return (_jsx(Layout, { children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100vh' }, children: [_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', marginBottom: 24 }, children: [_jsx("h1", { style: { margin: 0, marginRight: 24, marginBottom: 12 }, children: "Impersonate" }), _jsxs("div", { children: [_jsx("input", { type: "text", placeholder: "Search user by email or name", value: search, onChange: e => setSearch(e.target.value), onKeyDown: handleKeyDown, style: { minWidth: 300, marginRight: 8, height: '30px', paddingLeft: '4px' }, autoComplete: "impersonate-email" }), _jsx("button", { onClick: handleSearch, disabled: loading, children: "Search" })] })] }), error && _jsx("div", { style: { color: 'red', marginTop: 8 }, children: error }), _jsx("div", { style: { marginTop: 16, flex: 1, overflowY: 'auto', display: 'flex', flexDirection: 'column', gap: 16 }, children: results.map((user, idx) => (_jsxs("div", { style: {
|
49
|
+
border: '1px solid #ccc',
|
50
|
+
borderRadius: 8,
|
51
|
+
padding: 16,
|
52
|
+
minWidth: 260,
|
53
|
+
marginBottom: 8,
|
54
|
+
boxShadow: '0 2px 8px rgba(0,0,0,0.04)',
|
55
|
+
cursor: 'pointer',
|
56
|
+
background: '#111',
|
57
|
+
transition: 'box-shadow 0.2s',
|
58
|
+
userSelect: 'none',
|
59
|
+
height: 'min-content'
|
60
|
+
}, onClick: () => handleSelect(user.UserName), tabIndex: 0, onKeyDown: e => { if (e.key === 'Enter')
|
61
|
+
handleSelect(user.UserName); }, children: [_jsx("div", { style: { fontWeight: 600, fontSize: 16, marginBottom: 2 }, children: user.CustId }), _jsx("div", { style: { marginBottom: 2 }, children: user.UserName }), _jsx("div", { style: { color: '#777', fontSize: 14 }, children: user.CustName })] }, user.Id || user.email || idx))) })] }) }));
|
62
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
2
|
+
import { Link } from 'react-router-dom';
|
3
|
+
import { useAuthContext } from '../../lib';
|
4
|
+
export default function Layout({ children }) {
|
5
|
+
return (_jsxs("div", { style: {
|
6
|
+
minHeight: '100vh',
|
7
|
+
width: '100vw',
|
8
|
+
display: 'flex',
|
9
|
+
flexDirection: 'column',
|
10
|
+
}, children: [_jsx(Navigation, {}), _jsx("main", { style: {
|
11
|
+
flex: 1,
|
12
|
+
display: 'flex',
|
13
|
+
alignItems: 'center',
|
14
|
+
justifyContent: 'center',
|
15
|
+
width: '100%',
|
16
|
+
}, children: children })] }));
|
17
|
+
}
|
18
|
+
export function Navigation() {
|
19
|
+
const auth = useAuthContext();
|
20
|
+
return (_jsx("nav", { style: {
|
21
|
+
width: '100%',
|
22
|
+
background: '#222',
|
23
|
+
color: '#fff',
|
24
|
+
padding: '0.5rem 1rem',
|
25
|
+
marginBottom: '2rem',
|
26
|
+
boxSizing: 'border-box',
|
27
|
+
}, children: _jsxs("ul", { style: {
|
28
|
+
display: 'flex',
|
29
|
+
listStyle: 'none',
|
30
|
+
margin: 0,
|
31
|
+
padding: 0,
|
32
|
+
}, children: [_jsx("li", { style: { marginRight: '1.5rem' }, children: _jsx(Link, { to: "/", style: { color: '#fff', textDecoration: 'none' }, children: "Home" }) }), _jsx("li", { style: { marginRight: '1.5rem' }, children: _jsx(Link, { to: "/login", style: { color: '#fff', textDecoration: 'none' }, children: auth?.email ? _jsx(_Fragment, { children: auth.email }) : _jsx(_Fragment, { children: "Login" }) }) }), _jsx("li", { style: { marginRight: '1.5rem' }, children: _jsx(Link, { to: "/impersonate", style: { color: '#fff', textDecoration: 'none' }, children: "Impersonate" }) }), _jsx("li", { style: { marginRight: '1.5rem' }, children: _jsx(Link, { to: "/microflex", style: { color: '#fff', textDecoration: 'none' }, children: "MicroFlex" }) })] }) }));
|
33
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export default function Login(): import("react/jsx-runtime").JSX.Element;
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
+
import { useState } from 'react';
|
3
|
+
import { useAuthContext } from '../../lib/authentication/AuthContext';
|
4
|
+
import Layout from '../layout/Layout';
|
5
|
+
export default function Login() {
|
6
|
+
const auth = useAuthContext();
|
7
|
+
console.log(auth);
|
8
|
+
if (!auth || auth.userType === "Guest") {
|
9
|
+
return _jsx(LoginForm, {});
|
10
|
+
}
|
11
|
+
const userName = auth.claims?.Customer?.FirstName && auth.claims?.Customer?.LastName
|
12
|
+
? `${auth.claims.Customer.FirstName} ${auth.claims.Customer.LastName}`
|
13
|
+
: auth.claims?.Employee?.NickName || auth.claims?.Employee?.UserName || "";
|
14
|
+
return (_jsx(Layout, { children: _jsxs("div", { style: {
|
15
|
+
minWidth: 320,
|
16
|
+
maxWidth: 600,
|
17
|
+
width: '100%',
|
18
|
+
background: '#181818',
|
19
|
+
color: '#fff',
|
20
|
+
padding: 24,
|
21
|
+
borderRadius: 8,
|
22
|
+
boxShadow: '0 2px 8px rgba(0,0,0,0.2)',
|
23
|
+
overflow: 'auto',
|
24
|
+
// maxHeight: 500,
|
25
|
+
}, children: [_jsx("h1", { style: { marginBottom: 12 }, children: "Account" }), _jsxs("div", { style: { marginBottom: 12 }, children: [_jsx("strong", { children: "Email:" }), " ", auth.email] }), _jsxs("div", { style: { marginBottom: 12 }, children: [_jsx("strong", { children: "Name:" }), " ", userName] }), _jsxs("div", { style: { marginBottom: 12 }, children: [_jsx("strong", { children: "Claims:" }), _jsx("pre", { style: {
|
26
|
+
background: '#222',
|
27
|
+
color: '#fff',
|
28
|
+
padding: 12,
|
29
|
+
borderRadius: 4,
|
30
|
+
maxHeight: 300,
|
31
|
+
overflow: 'auto',
|
32
|
+
fontSize: 13,
|
33
|
+
}, children: JSON.stringify(auth.claims, null, 2) })] }), _jsxs("div", { style: { width: '100%', display: 'flex', flexDirection: 'row', gap: 8, justifyContent: 'center' }, children: [_jsx("button", { onClick: () => auth.logout(), children: "Logout" }), auth.userType === "Employee" && auth.claims?.Customer &&
|
34
|
+
_jsx("button", { onClick: () => auth.impersonate(null), children: "Cancel Impersonation" })] })] }) }));
|
35
|
+
}
|
36
|
+
function LoginForm() {
|
37
|
+
const auth = useAuthContext();
|
38
|
+
const [email, setEmail] = useState('');
|
39
|
+
const [password, setPassword] = useState('');
|
40
|
+
const [error, setError] = useState(null);
|
41
|
+
const handleSubmit = async (e) => {
|
42
|
+
e.preventDefault();
|
43
|
+
setError(null);
|
44
|
+
try {
|
45
|
+
await auth?.login(email, password);
|
46
|
+
// Optionally redirect or show success
|
47
|
+
}
|
48
|
+
catch (err) {
|
49
|
+
setError(err?.message || 'Login failed');
|
50
|
+
}
|
51
|
+
};
|
52
|
+
return (_jsx(Layout, { children: _jsxs("div", { style: { minWidth: 300 }, children: [_jsx("h1", { children: "Login" }), _jsxs("form", { onSubmit: handleSubmit, children: [_jsx("div", { style: { marginBottom: 12 }, children: _jsxs("label", { children: ["Email:", _jsx("input", { type: "email", value: email, onChange: e => setEmail(e.target.value), required: true, style: { width: '100%', height: '30px', paddingLeft: '4px' } })] }) }), _jsx("div", { style: { marginBottom: 12 }, children: _jsxs("label", { children: ["Password:", _jsx("input", { type: "password", value: password, onChange: e => setPassword(e.target.value), required: true, style: { width: '100%', height: '30px', paddingLeft: '4px' } })] }) }), error && _jsx("div", { style: { color: 'red', marginBottom: 12 }, children: error }), _jsx("button", { type: "submit", style: { width: '100%' }, children: "Login" })] })] }) }));
|
53
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export default function MicroFlex(): import("react/jsx-runtime").JSX.Element;
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
+
import { useState } from "react";
|
3
|
+
import useMicroFlex from "../../lib/hooks/useMicroFlex";
|
4
|
+
import AppSettings from "../util/AppSettings";
|
5
|
+
import { useAuthContext } from "../../lib";
|
6
|
+
import Layout from "../layout/Layout";
|
7
|
+
export default function MicroFlex() {
|
8
|
+
let port = '';
|
9
|
+
if (window.location.hostname.includes("localhost"))
|
10
|
+
port = window.location.port;
|
11
|
+
const auth = useAuthContext();
|
12
|
+
const apiURL = AppSettings.Endpoints.MicroFlexFormAuth(port);
|
13
|
+
// Ensure fetchFn is always defined and returns Promise<Response>
|
14
|
+
const fetchFn = (auth?.fetch ??
|
15
|
+
(async (_url, _options) => {
|
16
|
+
// fallback: throw error if fetch is not available
|
17
|
+
throw new Error("fetchFn is not defined");
|
18
|
+
}));
|
19
|
+
const { validate, cardToken, cardError, setupError, autoCompleteData, } = useMicroFlex({ initializeForm: !!auth?.initialized, apiURL, fetchFn });
|
20
|
+
const [validationResult, setValidationResult] = useState(undefined);
|
21
|
+
const [validationError, setValidationError] = useState(undefined);
|
22
|
+
const handleValidate = async () => {
|
23
|
+
setValidationResult(undefined);
|
24
|
+
setValidationError(undefined);
|
25
|
+
try {
|
26
|
+
const token = await validate();
|
27
|
+
setValidationResult(token);
|
28
|
+
}
|
29
|
+
catch (err) {
|
30
|
+
setValidationError(err?.message || String(err));
|
31
|
+
}
|
32
|
+
};
|
33
|
+
return (_jsx(Layout, { children: _jsxs("div", { style: { width: 400, maxWidth: 400 }, children: [_jsx("h2", { children: "MicroFlex Manual Test UI" }), _jsxs("div", { children: [_jsx("div", { id: "number", style: { height: 30, marginBottom: 12, borderRadius: 8, border: "1px solid #ccc", padding: 4 } }), _jsx("div", { id: "securityCode", style: { height: 30, marginBottom: 12, borderRadius: 8, border: "1px solid #ccc", padding: 4 } })] }), _jsx("button", { onClick: handleValidate, style: { marginBottom: 16 }, children: "Validate Card" }), _jsxs("div", { style: { marginBottom: 16 }, children: [validationResult && (_jsxs("div", { style: { color: "green", wordBreak: "break-all", whiteSpace: "pre-wrap", maxHeight: 200, overflow: "auto" }, children: ["Token: ", validationResult] })), validationError && (_jsxs("div", { style: { color: "red", wordBreak: "break-all", whiteSpace: "pre-wrap", maxHeight: 200, overflow: "auto" }, children: ["Validation Error: ", validationError] }))] }), _jsxs("div", { children: [_jsx("strong", { children: "Hook State:" }), _jsx("pre", { style: {
|
34
|
+
background: '#222',
|
35
|
+
color: '#fff',
|
36
|
+
padding: 12,
|
37
|
+
borderRadius: 4,
|
38
|
+
maxHeight: 300,
|
39
|
+
overflow: 'auto',
|
40
|
+
fontSize: 13,
|
41
|
+
}, children: JSON.stringify({
|
42
|
+
cardToken,
|
43
|
+
cardError,
|
44
|
+
setupError,
|
45
|
+
autoCompleteData,
|
46
|
+
}, null, 2) })] })] }) }));
|
47
|
+
}
|
@@ -0,0 +1,109 @@
|
|
1
|
+
interface IAuthentication {
|
2
|
+
ConfirmEmail: string;
|
3
|
+
ResendConfirmationEmail: string;
|
4
|
+
ForgotPassword: string;
|
5
|
+
ResetPassword: string;
|
6
|
+
Login: string;
|
7
|
+
Logout: string;
|
8
|
+
Register: string;
|
9
|
+
ImpersonateSearch: string;
|
10
|
+
Impersonate: (_email?: string | null) => string;
|
11
|
+
ForgotPasswordUrl: string;
|
12
|
+
}
|
13
|
+
interface ICatalog {
|
14
|
+
Sections: () => string;
|
15
|
+
Blocks: (_section: string) => string;
|
16
|
+
Parts: (_block: string) => string;
|
17
|
+
Part: (_item: string, _block: string) => string;
|
18
|
+
Search: (_input: string, _count?: number, _showAll?: boolean) => string;
|
19
|
+
SearchCount: (_input: string) => string;
|
20
|
+
Images: (_name?: string) => string;
|
21
|
+
Inventory: (_partId: string) => string;
|
22
|
+
InventoryBatch: () => string;
|
23
|
+
PartPrice: (_partId: string) => string;
|
24
|
+
PartPriceBatch: () => string;
|
25
|
+
}
|
26
|
+
interface ICasePics {
|
27
|
+
PartImages: (_itemKey: string) => string;
|
28
|
+
Report: (_report: string) => string;
|
29
|
+
TopUsers: () => string;
|
30
|
+
Progress: () => string;
|
31
|
+
SaveImages: () => string;
|
32
|
+
SignalRHub: () => string;
|
33
|
+
}
|
34
|
+
interface ICheckout {
|
35
|
+
AccountInfo: (_custId: string) => string;
|
36
|
+
VerifyOrder: () => string;
|
37
|
+
GetRequestId: () => string;
|
38
|
+
SubmitOrder: () => string;
|
39
|
+
}
|
40
|
+
interface ICustomParts {
|
41
|
+
GasketProfile: (_profile: string) => string;
|
42
|
+
Heaters: () => string;
|
43
|
+
Coils: () => string;
|
44
|
+
Shelves: () => string;
|
45
|
+
}
|
46
|
+
interface IIntegrations {
|
47
|
+
CybersourceUrl: string;
|
48
|
+
Loupe: string;
|
49
|
+
}
|
50
|
+
interface IEndpoints {
|
51
|
+
Exemptions: (_custId: string) => string;
|
52
|
+
ExemptionsUrl: () => string;
|
53
|
+
Users: () => string;
|
54
|
+
Addresses: () => string;
|
55
|
+
EmployeeUsers: () => string;
|
56
|
+
Payments: () => string;
|
57
|
+
OrderDetail: (_op: string, _requestId?: string) => string;
|
58
|
+
MicroFlexFormAuth: (_port: string | null) => string;
|
59
|
+
}
|
60
|
+
interface IImages {
|
61
|
+
WebSpin: (_itemId: string) => string;
|
62
|
+
SpinStill: (_itemId: string) => string;
|
63
|
+
Supplemental: (_itemId: string) => string;
|
64
|
+
}
|
65
|
+
interface IPages {
|
66
|
+
PartFinder: string;
|
67
|
+
Intranet: string;
|
68
|
+
Account: string;
|
69
|
+
UserManager: string;
|
70
|
+
Custom: string;
|
71
|
+
Faq: string;
|
72
|
+
About: string;
|
73
|
+
Contact: string;
|
74
|
+
Forms: string;
|
75
|
+
Dashboard: string;
|
76
|
+
OrdersChart: string;
|
77
|
+
ReportClient: string;
|
78
|
+
EmployeeFaq: string;
|
79
|
+
CsrFaq: string;
|
80
|
+
ForgotPassword: string;
|
81
|
+
Register: string;
|
82
|
+
Orders: string;
|
83
|
+
Benefits: string;
|
84
|
+
CustomCoilForm: string;
|
85
|
+
CustomDoorForm: string;
|
86
|
+
CustomShelfForm: string;
|
87
|
+
}
|
88
|
+
interface IAnalytics {
|
89
|
+
GaHostname: string;
|
90
|
+
}
|
91
|
+
interface IAppSettings {
|
92
|
+
CustomerSite: string;
|
93
|
+
Version: string;
|
94
|
+
Env: string;
|
95
|
+
IsProd: boolean;
|
96
|
+
SignalR: string;
|
97
|
+
Authentication: IAuthentication;
|
98
|
+
Catalog: ICatalog;
|
99
|
+
CasePics: ICasePics;
|
100
|
+
Checkout: ICheckout;
|
101
|
+
CustomParts: ICustomParts;
|
102
|
+
Integrations: IIntegrations;
|
103
|
+
Endpoints: IEndpoints;
|
104
|
+
Images: IImages;
|
105
|
+
Pages: IPages;
|
106
|
+
Analytics: IAnalytics;
|
107
|
+
}
|
108
|
+
declare const AppSettings: IAppSettings;
|
109
|
+
export default AppSettings;
|
@@ -0,0 +1,138 @@
|
|
1
|
+
import environment from "../../../environment";
|
2
|
+
const Version = "3.6";
|
3
|
+
const IsProd = environment === "Prod";
|
4
|
+
const IsLocalRequestServer = false;
|
5
|
+
const IsLocalApi = false;
|
6
|
+
const CustomerSite = IsProd ? "https://www.caseparts.com" : "https://dev.caseparts.com";
|
7
|
+
const ApiServer = IsLocalApi
|
8
|
+
? "https://localhost:44340/api/"
|
9
|
+
: IsProd
|
10
|
+
? "https://my.caseparts.com/core/api/"
|
11
|
+
: "https://mydev.caseparts.com/core/api/";
|
12
|
+
const BaseUrl = IsLocalRequestServer
|
13
|
+
? "https://localhost:44340/"
|
14
|
+
: IsProd
|
15
|
+
? "https://my.caseparts.com/core/"
|
16
|
+
: "https://mydev.caseparts.com/core/";
|
17
|
+
const MyServer = IsLocalRequestServer
|
18
|
+
? "http://localhost:44340"
|
19
|
+
: IsProd
|
20
|
+
? "https://my.caseparts.com"
|
21
|
+
: "https://mydev.caseparts.com";
|
22
|
+
const CatalogServer = IsLocalRequestServer
|
23
|
+
? "http://localhost:44340"
|
24
|
+
: IsProd
|
25
|
+
? "https://www.caseparts.com"
|
26
|
+
: "https://dev.caseparts.com";
|
27
|
+
// const MobileServer: string = environment === "local"
|
28
|
+
// ? "http://localhost:3000"
|
29
|
+
// : IsProd
|
30
|
+
// ? "https://mobile.caseparts.com"
|
31
|
+
// : "https://mobiledev.caseparts.com";
|
32
|
+
const CatalogImageServer = IsProd ? "https://www.caseparts.com/graphics" : "https://dev.caseparts.com/graphics";
|
33
|
+
const Api = (endpoint) => ApiServer + endpoint;
|
34
|
+
const SignalR = (hub) => BaseUrl + hub;
|
35
|
+
const My = (url) => MyServer + url;
|
36
|
+
const Main = (url) => CatalogServer + url;
|
37
|
+
const AppSettings = {
|
38
|
+
CustomerSite: CustomerSite,
|
39
|
+
Version: Version,
|
40
|
+
Env: environment,
|
41
|
+
IsProd: IsProd,
|
42
|
+
SignalR: SignalR("signalr/"),
|
43
|
+
Authentication: {
|
44
|
+
ConfirmEmail: Api("authenticate/confirmemail/"),
|
45
|
+
ResendConfirmationEmail: Api("authenticate/resendconfirmemail"),
|
46
|
+
ForgotPassword: Api("authenticate/forgotpassword"),
|
47
|
+
ResetPassword: Api("authenticate/resetpassword"),
|
48
|
+
Login: Api("authenticate/login"),
|
49
|
+
Logout: Api("authenticate/logout"),
|
50
|
+
Register: Api("authenticate/register"),
|
51
|
+
ImpersonateSearch: Api("myaccount/search?val="),
|
52
|
+
Impersonate: (_email) => _email ? Api("authenticate/impersonate?email=") + _email : Api("authenticate/impersonate"),
|
53
|
+
ForgotPasswordUrl: My("/account/forgotpassword"),
|
54
|
+
},
|
55
|
+
Catalog: {
|
56
|
+
Sections: () => Api("mobilecatalog/sections"),
|
57
|
+
Blocks: (_section) => Api(`mobilecatalog/blocks/${_section}`),
|
58
|
+
Parts: (_block) => Api(`mobilecatalog/block/${_block}/parts`),
|
59
|
+
Part: (_item, _block) => Api(`mobilecatalog/parts/${_item}/${_block}`),
|
60
|
+
Search: (_input, _count = 20, _showAll = false) => Api(`mobilecatalog/search/?keywords=${_input}&rowCount=${_count}&showAll=${_showAll}`),
|
61
|
+
SearchCount: (_input) => Api(`mobilecatalog/searchCount/?keywords=${_input}`),
|
62
|
+
Images: (_name) => `${CatalogImageServer}/${_name ?? ''}`,
|
63
|
+
Inventory: (_partId) => Api(`part/inventory/${_partId}`),
|
64
|
+
InventoryBatch: () => Api(`part/inventory/batch`),
|
65
|
+
PartPrice: (_partId) => Api(`part/pricing/${_partId}`),
|
66
|
+
PartPriceBatch: () => Api(`part/pricing`),
|
67
|
+
},
|
68
|
+
CasePics: {
|
69
|
+
PartImages: (_itemKey) => Api(`casepics/part/${_itemKey}`),
|
70
|
+
Report: (_report) => Api(`casepics/report/${_report}`),
|
71
|
+
TopUsers: () => Api("casepics/top-users"),
|
72
|
+
Progress: () => Api("casepics/progress"),
|
73
|
+
SaveImages: () => Api("casepics/save"),
|
74
|
+
SignalRHub: () => SignalR("casepicshub"),
|
75
|
+
},
|
76
|
+
Checkout: {
|
77
|
+
AccountInfo: (_custId) => Api(`checkout/GetCheckoutInfo?customerid=${_custId}`),
|
78
|
+
VerifyOrder: () => Api("checkout/VerifyOrder"),
|
79
|
+
GetRequestId: () => Api("requests"),
|
80
|
+
SubmitOrder: () => Api("checkout/CreateQuote"),
|
81
|
+
},
|
82
|
+
CustomParts: {
|
83
|
+
GasketProfile: (_profile) => `${CatalogImageServer}%2fDrawings%5c${_profile}.png`,
|
84
|
+
Heaters: () => Api("part/heaters"),
|
85
|
+
Coils: () => Api("mobilecatalog/block/evaporator-coils-index/parts"),
|
86
|
+
Shelves: () => Api("mobilecatalog/block/wire-shelf-index/parts"),
|
87
|
+
},
|
88
|
+
Integrations: {
|
89
|
+
CybersourceUrl: Api("cybersource"),
|
90
|
+
Loupe: `${MyServer}/core`,
|
91
|
+
},
|
92
|
+
Endpoints: {
|
93
|
+
Exemptions: (_custId) => Api(`avatax/customers/${_custId}/exemptions`),
|
94
|
+
ExemptionsUrl: () => Api("avatax/certexpress"),
|
95
|
+
Users: () => Api("myaccount/users"),
|
96
|
+
Addresses: () => Api("myaccount/addresses"),
|
97
|
+
EmployeeUsers: () => Api("myaccount/internal/users"),
|
98
|
+
Payments: () => Api("myaccount/cards"),
|
99
|
+
OrderDetail: (_op, _requestId) => {
|
100
|
+
let url = Api(`myaccount/order/view/${_op}`);
|
101
|
+
url += _requestId ? `/${_requestId}` : "";
|
102
|
+
return url;
|
103
|
+
},
|
104
|
+
MicroFlexFormAuth: (_port) => Api(`cybersource/publickey/${_port || ''}`)
|
105
|
+
},
|
106
|
+
Images: {
|
107
|
+
WebSpin: (_itemId) => `${CatalogServer}/Ortery/360/Optimized/${_itemId}_/`,
|
108
|
+
SpinStill: (_itemId) => `${CatalogServer}/Ortery/360/Optimized/${_itemId}_/`,
|
109
|
+
Supplemental: (_itemId) => `${CatalogServer}/Ortery/2D/Optimized/${_itemId}/`,
|
110
|
+
},
|
111
|
+
Pages: {
|
112
|
+
PartFinder: Main(""),
|
113
|
+
Intranet: My("/home"),
|
114
|
+
Account: Main("/account"),
|
115
|
+
UserManager: Main("/account/cpc/users"),
|
116
|
+
Custom: Main("/custom"),
|
117
|
+
Faq: Main("/faq"),
|
118
|
+
About: Main("/about"),
|
119
|
+
Contact: Main("/account/contactus"),
|
120
|
+
Forms: Main("/forms"),
|
121
|
+
Dashboard: Main("/dash"),
|
122
|
+
OrdersChart: Main("/orders"),
|
123
|
+
ReportClient: My("/reports"),
|
124
|
+
EmployeeFaq: Main("/cpc"),
|
125
|
+
CsrFaq: Main("/csr"),
|
126
|
+
ForgotPassword: Main("/account/forgotpassword"),
|
127
|
+
Register: Main("/account/register"),
|
128
|
+
Orders: Main("/account/orders"),
|
129
|
+
Benefits: Main("/account/benefits"),
|
130
|
+
CustomCoilForm: My("/pdf/Custom Coil Order Form.pdf"),
|
131
|
+
CustomDoorForm: My("/pdf/Walk-In%20Door%20Order%20Form.pdf"),
|
132
|
+
CustomShelfForm: My("/pdf/Wire%20Shelf%20Order%20Form.pdf"),
|
133
|
+
},
|
134
|
+
Analytics: {
|
135
|
+
GaHostname: "www.caseparts.com",
|
136
|
+
},
|
137
|
+
};
|
138
|
+
export default AppSettings;
|
@@ -0,0 +1 @@
|
|
1
|
+
import './index.css';
|
package/dist/src/main.js
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
import { StrictMode } from 'react';
|
3
|
+
import { createRoot } from 'react-dom/client';
|
4
|
+
import './index.css';
|
5
|
+
import App from './App';
|
6
|
+
createRoot(document.getElementById('root')).render(_jsx(StrictMode, { children: _jsx(App, {}) }));
|
package/package.json
CHANGED
@@ -1,53 +1,54 @@
|
|
1
|
-
{
|
2
|
-
"name": "@caseparts-org/casecore",
|
3
|
-
"private": false,
|
4
|
-
"version": "0.0.
|
5
|
-
"type": "module",
|
6
|
-
"main": "dist/index.js",
|
7
|
-
"types": "dist/index.d.ts",
|
8
|
-
"exports": {
|
9
|
-
".": {
|
10
|
-
"types": "./dist/index.d.ts",
|
11
|
-
"import": "./dist/index.js",
|
12
|
-
"require": "./dist/index.js"
|
13
|
-
}
|
14
|
-
},
|
15
|
-
"files": [
|
16
|
-
"dist"
|
17
|
-
],
|
18
|
-
"peerDependencies": {
|
19
|
-
"react": "^18.0.0"
|
20
|
-
},
|
21
|
-
"scripts": {
|
22
|
-
"dev": "vite",
|
23
|
-
"build": "tsc -p tsconfig.json",
|
24
|
-
"lint": "eslint .",
|
25
|
-
"preview": "vite preview",
|
26
|
-
"test": "vitest",
|
27
|
-
"test:ci": "vitest run"
|
28
|
-
},
|
29
|
-
"dependencies": {
|
30
|
-
"jwt-decode": "^4.0.0",
|
31
|
-
"react": "^18.0.0",
|
32
|
-
"react-dom": "^18.0.0",
|
33
|
-
"uuid": "^11.1.0"
|
34
|
-
},
|
35
|
-
"devDependencies": {
|
36
|
-
"@eslint/js": "^9.29.0",
|
37
|
-
"@testing-library/jest-dom": "^6.6.3",
|
38
|
-
"@testing-library/react": "^16.3.0",
|
39
|
-
"@types/node": "^24.0.4",
|
40
|
-
"@types/react": "^19.1.8",
|
41
|
-
"@types/react-dom": "^19.1.6",
|
42
|
-
"@vitejs/plugin-react": "^4.5.2",
|
43
|
-
"eslint": "^9.29.0",
|
44
|
-
"eslint-plugin-react-hooks": "^5.2.0",
|
45
|
-
"eslint-plugin-react-refresh": "^0.4.20",
|
46
|
-
"globals": "^16.2.0",
|
47
|
-
"jsdom": "^26.1.0",
|
48
|
-
"
|
49
|
-
"typescript
|
50
|
-
"
|
51
|
-
"
|
52
|
-
|
53
|
-
}
|
1
|
+
{
|
2
|
+
"name": "@caseparts-org/casecore",
|
3
|
+
"private": false,
|
4
|
+
"version": "0.0.6",
|
5
|
+
"type": "module",
|
6
|
+
"main": "dist/index.js",
|
7
|
+
"types": "dist/index.d.ts",
|
8
|
+
"exports": {
|
9
|
+
".": {
|
10
|
+
"types": "./dist/index.d.ts",
|
11
|
+
"import": "./dist/index.js",
|
12
|
+
"require": "./dist/index.js"
|
13
|
+
}
|
14
|
+
},
|
15
|
+
"files": [
|
16
|
+
"dist"
|
17
|
+
],
|
18
|
+
"peerDependencies": {
|
19
|
+
"react": "^18.0.0"
|
20
|
+
},
|
21
|
+
"scripts": {
|
22
|
+
"dev": "vite",
|
23
|
+
"build": "tsc -p tsconfig.json",
|
24
|
+
"lint": "eslint .",
|
25
|
+
"preview": "vite preview",
|
26
|
+
"test": "vitest",
|
27
|
+
"test:ci": "vitest run"
|
28
|
+
},
|
29
|
+
"dependencies": {
|
30
|
+
"jwt-decode": "^4.0.0",
|
31
|
+
"react": "^18.0.0",
|
32
|
+
"react-dom": "^18.0.0",
|
33
|
+
"uuid": "^11.1.0"
|
34
|
+
},
|
35
|
+
"devDependencies": {
|
36
|
+
"@eslint/js": "^9.29.0",
|
37
|
+
"@testing-library/jest-dom": "^6.6.3",
|
38
|
+
"@testing-library/react": "^16.3.0",
|
39
|
+
"@types/node": "^24.0.4",
|
40
|
+
"@types/react": "^19.1.8",
|
41
|
+
"@types/react-dom": "^19.1.6",
|
42
|
+
"@vitejs/plugin-react": "^4.5.2",
|
43
|
+
"eslint": "^9.29.0",
|
44
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
45
|
+
"eslint-plugin-react-refresh": "^0.4.20",
|
46
|
+
"globals": "^16.2.0",
|
47
|
+
"jsdom": "^26.1.0",
|
48
|
+
"react-router-dom": "^7.6.3",
|
49
|
+
"typescript": "~5.8.3",
|
50
|
+
"typescript-eslint": "^8.34.1",
|
51
|
+
"vite": "^7.0.0",
|
52
|
+
"vitest": "^3.2.4"
|
53
|
+
}
|
54
|
+
}
|
File without changes
|
@@ -1 +0,0 @@
|
|
1
|
-
"use strict";
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|