@campxdev/shared 3.1.23 → 3.1.24-alpha.2
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/CLAUDE.md +202 -0
- package/package.json +3 -1
- package/src/config/axios.ts +12 -1
- package/src/config/axiosEvaluator.ts +10 -1
- package/src/config/axiosXTenant.ts +10 -0
- package/src/utils/clientAuthToken.ts +17 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
**Package**: `@campxdev/shared` (v3.1.23)
|
|
6
|
+
**Entry point**: `./exports.ts`
|
|
7
|
+
**Type**: Shared React component library (internally called "X1 UI package")
|
|
8
|
+
|
|
9
|
+
This is a shared frontend library consumed by **all CampX frontend microfrontends** (27+ apps). It provides common React components, hooks, utilities, contexts, layouts, and theme configuration. It is the **legacy** shared UI layer (X1, built on MUI + styled-components). The newer design system is `@campxdev/react-blueprint` (X3), but this package remains widely used and actively maintained.
|
|
10
|
+
|
|
11
|
+
**Consumers**: Every `campx-*-web` frontend repo imports from `@campxdev/shared`. Changes here propagate across the entire platform.
|
|
12
|
+
|
|
13
|
+
## Architecture
|
|
14
|
+
|
|
15
|
+
### Package Structure
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
src/
|
|
19
|
+
├── components/ # Reusable UI components (the bulk of this package)
|
|
20
|
+
├── hooks/ # Custom React hooks
|
|
21
|
+
├── utils/ # Helper functions, formatters, parsers
|
|
22
|
+
├── contexts/ # React context providers (auth, tenant, etc.)
|
|
23
|
+
├── layouts/ # Page layout shells and wrappers
|
|
24
|
+
├── theme/ # MUI theme configuration and overrides
|
|
25
|
+
├── config/ # Axios instances, app config
|
|
26
|
+
├── constants/ # Shared constants and enums
|
|
27
|
+
├── shared-state/ # Pullstate global stores
|
|
28
|
+
├── permissions/ # Permission definitions and helpers
|
|
29
|
+
├── sitemap/ # Route/navigation definitions
|
|
30
|
+
├── assets/ # Shared icons, images, static files
|
|
31
|
+
├── stories/ # Storybook stories
|
|
32
|
+
├── pages/ # Shared pages (login, error, profile, etc.)
|
|
33
|
+
├── App.tsx # Dev/preview app (not exported)
|
|
34
|
+
├── index.tsx # Dev server entry point (not exported)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Entry Point
|
|
38
|
+
|
|
39
|
+
The **published entry point** is `exports.ts` (at project root, NOT `src/index.tsx`). This file re-exports everything consumers can import. `src/index.tsx` is only for local dev/preview via `react-scripts start`.
|
|
40
|
+
|
|
41
|
+
When adding new exports, **always add them to `exports.ts`** — otherwise consumers cannot access them.
|
|
42
|
+
|
|
43
|
+
### Key Technology Stack
|
|
44
|
+
|
|
45
|
+
- **React 18** with TypeScript
|
|
46
|
+
- **MUI v7** (`@mui/material`, `@mui/icons-material`, `@mui/lab`, `@mui/x-date-pickers`)
|
|
47
|
+
- **Emotion** (`@emotion/react`, `@emotion/styled`) — MUI's styling engine
|
|
48
|
+
- **styled-components** — also present for legacy styled components
|
|
49
|
+
- **react-hook-form** + **yup** — form state and validation
|
|
50
|
+
- **react-query v3** — server state management
|
|
51
|
+
- **react-table v7** — table components
|
|
52
|
+
- **Pullstate** — lightweight global state
|
|
53
|
+
- **Axios** — HTTP client
|
|
54
|
+
- **react-router-dom v6** — routing utilities
|
|
55
|
+
- **react-toastify** — toast notifications
|
|
56
|
+
- **Moment.js** — date handling
|
|
57
|
+
- **Fuse.js** — fuzzy search
|
|
58
|
+
- **Storybook 6** — component documentation (dev only)
|
|
59
|
+
|
|
60
|
+
## Key Commands
|
|
61
|
+
|
|
62
|
+
| Command | Purpose |
|
|
63
|
+
|---------|---------|
|
|
64
|
+
| `yarn start` / `yarn dev` | Start local dev server (CRA, port 3000) for previewing components |
|
|
65
|
+
| `yarn build` | Production build (`CI=false && react-scripts build`) |
|
|
66
|
+
| `yarn test` | Run tests via react-scripts |
|
|
67
|
+
| `yarn lint` | ESLint check across all src files |
|
|
68
|
+
| `yarn lint:fix` | Auto-fix lint issues |
|
|
69
|
+
| `yarn format` | Prettier format all source files |
|
|
70
|
+
| `yarn storybook` | Start Storybook on port 6006 |
|
|
71
|
+
| `yarn build-storybook` | Build static Storybook site |
|
|
72
|
+
|
|
73
|
+
### Local Linking Workflow (yalc)
|
|
74
|
+
|
|
75
|
+
This is a **library package** — it is not deployed standalone. To test changes in a consuming app:
|
|
76
|
+
|
|
77
|
+
1. **In this repo**: make changes, then run:
|
|
78
|
+
```bash
|
|
79
|
+
yalc push
|
|
80
|
+
```
|
|
81
|
+
2. **In the consuming repo** (e.g., `campx-common-workspace`):
|
|
82
|
+
```bash
|
|
83
|
+
yalc add @campxdev/shared
|
|
84
|
+
yarn install
|
|
85
|
+
```
|
|
86
|
+
3. After iterating, repeat `yalc push` — consuming repos with `yalc link` will auto-update.
|
|
87
|
+
4. **Before committing** in the consuming repo, remove yalc link:
|
|
88
|
+
```bash
|
|
89
|
+
yalc remove @campxdev/shared
|
|
90
|
+
yarn install
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
> **Note**: The `CI=false` in the build script suppresses treating warnings as errors. This is intentional for CRA builds.
|
|
94
|
+
|
|
95
|
+
## Exports
|
|
96
|
+
|
|
97
|
+
The `exports.ts` file is the source of truth. Exports are organized by category:
|
|
98
|
+
|
|
99
|
+
### Components (`src/components/`)
|
|
100
|
+
|
|
101
|
+
The largest export surface. Key component groups:
|
|
102
|
+
|
|
103
|
+
- **Layout & Navigation**: `PageHeader`, `PageContent`, `Breadcrumbs`, `Layout/*`, `Tabs/*`, `StepsHeader/*`
|
|
104
|
+
- **Forms**: `HookForm/*` (react-hook-form wrappers), `Form/*`, `Input/*`, `Selectors/*`, `ImageUpload`, `ExcelToJsonInput/*`
|
|
105
|
+
- **Tables**: `Table`, `Tables/*`, `StyledTableContainer`, `CustomizeReport/*`
|
|
106
|
+
- **Feedback**: `Spinner`, `FullScreenLoader`, `LinearProgress`, `ErrorBox`, `ErrorModal`, `ErrorModalWrapper/*`, `ErrorBoundary/*`, `NoDataIllustration`
|
|
107
|
+
- **Overlays**: `DrawerWrapper/*`, `PopupConfirm/*`, `UploadFileDialog/*`, `ModalButtons/*`
|
|
108
|
+
- **Data Display**: `Card`, `CardsGrid`, `Detail`, `DetailsGrid`, `LabelValue`, `JsonPreview`, `Chips`, `IconLabel`
|
|
109
|
+
- **Actions**: `ActionButton`, `IconButtons/*`, `DropDownButton/*`, `UploadButton/*`, `SwitchButton`, `ListItemButton`
|
|
110
|
+
- **Domain-specific**: `StudentCard/*`, `FeeCard`, `LoginForm`, `ChangePassword`, `ResetPassword`, `MyProfile/*`, `ActiveDevices/*`, `ActivityLog/*`, `ApplicationProfile/*`, `MongoCharts/*`, `Institutions/*`, `DatabaseConfiguration/*`
|
|
111
|
+
- **Misc**: `Attachment`, `FloatingContainer`, `DividerHeading`, `ReportHeader`, `SignatureFooter`, `ReactJoyRide`, `ToastContainer/*`, `MediaRow/*`, `AutocompleteSearch/*`, `FilterComponents/*`
|
|
112
|
+
|
|
113
|
+
### Hooks (`src/hooks/`)
|
|
114
|
+
|
|
115
|
+
Custom hooks for common patterns — data fetching wrappers, form utilities, UI state, permissions checks, etc.
|
|
116
|
+
|
|
117
|
+
### Utilities (`src/utils/`)
|
|
118
|
+
|
|
119
|
+
Helper functions: formatters (dates, currency, names), validators, file handling, API helpers, permission checks, etc.
|
|
120
|
+
|
|
121
|
+
### Contexts (`src/contexts/`)
|
|
122
|
+
|
|
123
|
+
React context providers for auth state, tenant info, app-level configuration that wraps consuming apps.
|
|
124
|
+
|
|
125
|
+
### Shared State (`src/shared-state/`)
|
|
126
|
+
|
|
127
|
+
Pullstate stores shared across the app — user state, UI state, notifications, etc.
|
|
128
|
+
|
|
129
|
+
### Theme (`src/theme/`)
|
|
130
|
+
|
|
131
|
+
MUI theme object, palette definitions, typography overrides, component style overrides. Consumers wrap their app with this theme.
|
|
132
|
+
|
|
133
|
+
### Permissions (`src/permissions/`)
|
|
134
|
+
|
|
135
|
+
Permission constants and helper utilities for role-based access control across the CampX platform.
|
|
136
|
+
|
|
137
|
+
### Config (`src/config/`)
|
|
138
|
+
|
|
139
|
+
Axios instance configuration, API base URL setup, app-level config constants.
|
|
140
|
+
|
|
141
|
+
### Constants (`src/constants/`)
|
|
142
|
+
|
|
143
|
+
Shared enums, magic strings, configuration constants used across multiple frontend apps.
|
|
144
|
+
|
|
145
|
+
### Sitemap (`src/sitemap/`)
|
|
146
|
+
|
|
147
|
+
Route definitions and navigation structure shared across microfrontends.
|
|
148
|
+
|
|
149
|
+
## Versioning
|
|
150
|
+
|
|
151
|
+
- **Current version**: 3.1.23
|
|
152
|
+
- **Semver conventions**:
|
|
153
|
+
- **Patch** (3.1.x): Bug fixes, style tweaks, non-breaking additions
|
|
154
|
+
- **Minor** (3.x.0): New components, new hooks, new exports (backward-compatible)
|
|
155
|
+
- **Major** (x.0.0): Breaking changes to existing component APIs, removed exports, theme changes that affect all consumers
|
|
156
|
+
- **Bump version** in `package.json` before publishing
|
|
157
|
+
- **Publishing**: Handled via CI/GitHub Actions — do not publish manually to npm
|
|
158
|
+
|
|
159
|
+
### Breaking Change Considerations
|
|
160
|
+
|
|
161
|
+
Since 27+ microfrontends consume this package, **breaking changes are high-impact**:
|
|
162
|
+
- Never rename or remove an exported component/hook/utility without coordinating migration across all consumers
|
|
163
|
+
- Never change component prop interfaces in a breaking way without a major version bump
|
|
164
|
+
- Prefer adding new props with defaults over modifying existing behavior
|
|
165
|
+
- Deprecate before removing — add console warnings in the interim
|
|
166
|
+
|
|
167
|
+
## Consuming Repo Conventions
|
|
168
|
+
|
|
169
|
+
Patterns that consuming repos follow (and that this package must support):
|
|
170
|
+
|
|
171
|
+
- **Imports**: Consumers import directly from the package name:
|
|
172
|
+
```tsx
|
|
173
|
+
import { PageHeader, useAuth, formatDate } from '@campxdev/shared'
|
|
174
|
+
```
|
|
175
|
+
- **Theme**: Consumers wrap their app with the theme provider exported from this package
|
|
176
|
+
- **Axios**: Consumers may use the Axios instance from this package's config or from `@campxdev/campx-web-utils`
|
|
177
|
+
- **react-hook-form**: Form components expect react-hook-form v7 context. Consumers use `useForm` from react-hook-form and pass `control`/`register` to shared form components
|
|
178
|
+
- **react-query v3**: Hooks that use react-query expect a `QueryClientProvider` in the consumer's tree
|
|
179
|
+
- **react-router-dom v6**: Components that use routing (Breadcrumbs, Layout, etc.) expect a v6 router context
|
|
180
|
+
- **MUI v7**: Consumers must have compatible MUI v7 peer dependencies
|
|
181
|
+
- **Multi-tenancy**: All API-calling utilities must respect tenant context. Never hardcode tenant-specific values
|
|
182
|
+
- **Pullstate**: Consumers share Pullstate stores exported from this package for cross-cutting concerns (user state, etc.)
|
|
183
|
+
|
|
184
|
+
## Code Style
|
|
185
|
+
|
|
186
|
+
- **TypeScript** throughout — avoid `any` types; prefer explicit interfaces
|
|
187
|
+
- **ESLint + Prettier** configured — run `yarn lint:fix` and `yarn format` before committing
|
|
188
|
+
- **Prettier config**: `.prettierrc` at project root
|
|
189
|
+
- **Component pattern**: Functional components with hooks, no class components
|
|
190
|
+
- **Styling**: Prefer MUI `sx` prop or Emotion `styled()`. styled-components exists but is legacy — prefer Emotion for new code
|
|
191
|
+
|
|
192
|
+
## Storybook
|
|
193
|
+
|
|
194
|
+
Storybook is available for component documentation and visual testing:
|
|
195
|
+
- `yarn storybook` — starts on port 6006
|
|
196
|
+
- Stories live in `src/stories/`
|
|
197
|
+
- Uses `storybook-addon-react-router-v6` for components requiring router context
|
|
198
|
+
- `cross-env NODE_OPTIONS=--openssl-legacy-provider` is needed for the build due to Storybook 6 + Node 18+ compatibility
|
|
199
|
+
|
|
200
|
+
## Last Updated
|
|
201
|
+
|
|
202
|
+
2026-03-13
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@campxdev/shared",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.24-alpha.2",
|
|
4
4
|
"main": "./exports.ts",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"start": "react-scripts start",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"@mui/material": "^7.0.2",
|
|
37
37
|
"@mui/x-date-pickers": "^8.1.0",
|
|
38
38
|
"axios": "^1.7.2",
|
|
39
|
+
"crypto-js": "^4.2.0",
|
|
39
40
|
"device-detector-js": "^3.0.3",
|
|
40
41
|
"fuse.js": "^6.6.2",
|
|
41
42
|
"js-cookie": "^3.0.5",
|
|
@@ -69,6 +70,7 @@
|
|
|
69
70
|
"@storybook/preset-create-react-app": "^4.0.0",
|
|
70
71
|
"@storybook/react": "^6.5.14",
|
|
71
72
|
"@storybook/testing-library": "^0.0.13",
|
|
73
|
+
"@types/crypto-js": "^4.2.2",
|
|
72
74
|
"@types/js-cookie": "^3.0.6",
|
|
73
75
|
"@types/node": "^18.11.8",
|
|
74
76
|
"@types/react": "^18.3.3",
|
package/src/config/axios.ts
CHANGED
|
@@ -2,6 +2,7 @@ import Axios, { InternalAxiosRequestConfig } from 'axios'
|
|
|
2
2
|
import Cookies from 'js-cookie'
|
|
3
3
|
import { toast } from 'react-toastify'
|
|
4
4
|
import { NetworkStore } from '../components/ErrorBoundary/GlobalNetworkLoadingIndicator'
|
|
5
|
+
import { generateClientToken } from '../utils/clientAuthToken'
|
|
5
6
|
|
|
6
7
|
// Declare the extended axios types to include our custom property
|
|
7
8
|
declare module 'axios' {
|
|
@@ -95,6 +96,16 @@ axios.interceptors.request.use(
|
|
|
95
96
|
config.headers.set('campx_open_payments_key', openPaymentsKey)
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
// Inject client authentication token
|
|
100
|
+
const campxClientId = process.env.REACT_APP_CLIENT_ID
|
|
101
|
+
const campxClientSecret = process.env.REACT_APP_CAMPX_CLIENT_SECRET
|
|
102
|
+
if (campxClientId && campxClientSecret) {
|
|
103
|
+
config.headers.set(
|
|
104
|
+
'x-campx-client',
|
|
105
|
+
generateClientToken(campxClientId, campxClientSecret),
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
98
109
|
return {
|
|
99
110
|
...config,
|
|
100
111
|
params,
|
|
@@ -138,7 +149,7 @@ export const axiosErrorToast = (error: any, fallback?: string) => {
|
|
|
138
149
|
const fallbackMessage = fallback ?? 'Something went wrong.'
|
|
139
150
|
const errorMessage =
|
|
140
151
|
typeof error?.response?.data?.message !== 'string'
|
|
141
|
-
?
|
|
152
|
+
? error?.response?.data?.message?.join('\n') ?? fallbackMessage
|
|
142
153
|
: error?.response?.data?.message
|
|
143
154
|
|
|
144
155
|
return toast.error(errorMessage)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Axios from 'axios'
|
|
2
2
|
import Cookies from 'js-cookie'
|
|
3
3
|
import { NetworkStore } from '../components/ErrorBoundary/GlobalNetworkLoadingIndicator'
|
|
4
|
-
import {
|
|
4
|
+
import { generateClientToken } from '../utils/clientAuthToken'
|
|
5
5
|
import { formatParams } from './axios'
|
|
6
6
|
|
|
7
7
|
// const sessionKey = Cookies.get('campx_session_key')
|
|
@@ -25,6 +25,15 @@ axiosEvaluator.interceptors.request.use(
|
|
|
25
25
|
NetworkStore.update((s) => {
|
|
26
26
|
s.loading = true
|
|
27
27
|
})
|
|
28
|
+
// Inject client authentication token
|
|
29
|
+
const campxClientId = process.env.REACT_APP_CLIENT_ID
|
|
30
|
+
const campxClientSecret = process.env.REACT_APP_CAMPX_CLIENT_SECRET
|
|
31
|
+
if (campxClientId && campxClientSecret) {
|
|
32
|
+
config.headers.set(
|
|
33
|
+
'x-campx-client',
|
|
34
|
+
generateClientToken(campxClientId, campxClientSecret),
|
|
35
|
+
)
|
|
36
|
+
}
|
|
28
37
|
return { ...config, params }
|
|
29
38
|
},
|
|
30
39
|
function (error) {
|
|
@@ -2,6 +2,7 @@ import Axios from 'axios'
|
|
|
2
2
|
import Cookies from 'js-cookie'
|
|
3
3
|
import { NetworkStore } from '../components/ErrorBoundary/GlobalNetworkLoadingIndicator'
|
|
4
4
|
import { isDevelopment } from '../constants'
|
|
5
|
+
import { generateClientToken } from '../utils/clientAuthToken'
|
|
5
6
|
import { formatParams } from './axios'
|
|
6
7
|
|
|
7
8
|
const sessionKey = Cookies.get('campx_session_key')
|
|
@@ -33,6 +34,15 @@ axiosTenant.interceptors.request.use(
|
|
|
33
34
|
NetworkStore.update((s) => {
|
|
34
35
|
s.loading = true
|
|
35
36
|
})
|
|
37
|
+
// Inject client authentication token
|
|
38
|
+
const campxClientId = process.env.REACT_APP_CLIENT_ID
|
|
39
|
+
const campxClientSecret = process.env.REACT_APP_CAMPX_CLIENT_SECRET
|
|
40
|
+
if (campxClientId && campxClientSecret) {
|
|
41
|
+
config.headers.set(
|
|
42
|
+
'x-campx-client',
|
|
43
|
+
generateClientToken(campxClientId, campxClientSecret),
|
|
44
|
+
)
|
|
45
|
+
}
|
|
36
46
|
return { ...config, params }
|
|
37
47
|
},
|
|
38
48
|
function (error) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import CryptoJS from 'crypto-js'
|
|
2
|
+
|
|
3
|
+
function toBase64Url(base64: string): string {
|
|
4
|
+
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function generateClientToken(clientId: string, secret: string): string {
|
|
8
|
+
const payload = { clientId, iat: Math.floor(Date.now() / 1000) }
|
|
9
|
+
const payloadBase64url = toBase64Url(btoa(JSON.stringify(payload)))
|
|
10
|
+
|
|
11
|
+
const signatureWordArray = CryptoJS.HmacSHA256(payloadBase64url, secret)
|
|
12
|
+
const signatureBase64url = toBase64Url(
|
|
13
|
+
CryptoJS.enc.Base64.stringify(signatureWordArray),
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
return `${payloadBase64url}.${signatureBase64url}`
|
|
17
|
+
}
|