astrogators-shared-ui 0.5.0 → 0.7.0-rc.0

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 (30) hide show
  1. package/README.md +132 -196
  2. package/dist/index.css +2 -0
  3. package/dist/index.js +732 -805
  4. package/dist/{components → src/components}/display/Badge.d.ts +0 -1
  5. package/dist/{components → src/components}/display/Card.d.ts +0 -1
  6. package/dist/{components → src/components}/display/Modal.d.ts +0 -1
  7. package/dist/{components → src/components}/feedback/Loader.d.ts +0 -1
  8. package/dist/{components → src/components}/forms/AllyCodeDropdown.d.ts +0 -1
  9. package/dist/{components → src/components}/forms/Button.d.ts +0 -1
  10. package/dist/{components → src/components}/forms/Input.d.ts +0 -1
  11. package/dist/{components → src/components}/forms/Select.d.ts +0 -1
  12. package/dist/{components → src/components}/layout/Container.d.ts +0 -1
  13. package/dist/{components → src/components}/layout/Footer.d.ts +0 -1
  14. package/dist/{components → src/components}/layout/TopBar.d.ts +0 -1
  15. package/dist/{contexts → src/contexts}/AuthContext.d.ts +0 -1
  16. package/dist/{index.d.ts → src/index.d.ts} +0 -1
  17. package/package.json +22 -14
  18. package/dist/style.css +0 -1
  19. /package/dist/{components → src/components}/display/index.d.ts +0 -0
  20. /package/dist/{components → src/components}/feedback/index.d.ts +0 -0
  21. /package/dist/{components → src/components}/forms/index.d.ts +0 -0
  22. /package/dist/{components → src/components}/layout/index.d.ts +0 -0
  23. /package/dist/{services → src/services}/allyCodeStorage.d.ts +0 -0
  24. /package/dist/{services → src/services}/api.d.ts +0 -0
  25. /package/dist/{services → src/services}/auth.d.ts +0 -0
  26. /package/dist/{types → src/types}/api.d.ts +0 -0
  27. /package/dist/{types → src/types}/index.d.ts +0 -0
  28. /package/dist/{types → src/types}/mod.d.ts +0 -0
  29. /package/dist/{types → src/types}/user.d.ts +0 -0
  30. /package/dist/{utils → src/utils}/formatAllyCode.d.ts +0 -0
package/README.md CHANGED
@@ -1,169 +1,116 @@
1
1
  # @psytor/astrogators-shared-ui
2
2
 
3
- Shared UI components and utilities for Astrogator's Table applications.
3
+ Shared React components, auth, and API client for the Astrogator's Table
4
+ frontends (`astrogators-hub`, `mod-ledger-ui`). Published to GitHub Packages.
5
+
6
+ This is a library — there is no app shell here. See `PUBLISHING.md` for the
7
+ release flow and `CLAUDE.md` for architecture notes.
4
8
 
5
9
  ## Installation
6
10
 
7
- ```bash
8
- npm install @psytor/astrogators-shared-ui
11
+ The package is hosted on GitHub Packages, so consumers need an `.npmrc`
12
+ pointing the `@psytor` scope at the right registry:
13
+
14
+ ```
15
+ @psytor:registry=https://npm.pkg.github.com
16
+ //npm.pkg.github.com/:_authToken=${GITHUB_PAT}
9
17
  ```
10
18
 
11
- ## Setup
19
+ The PAT needs `read:packages` scope. Then:
12
20
 
13
- ### 1. Initialize API Client
21
+ ```bash
22
+ npm install @psytor/astrogators-shared-ui
23
+ ```
14
24
 
15
- In your application's entry point (e.g., `main.tsx` or `App.tsx`):
25
+ Peer dependencies: `react` and `react-dom` (18 or 19).
16
26
 
17
- ```tsx
18
- import { initializeApiClient } from '@psytor/astrogators-shared-ui';
27
+ ## Setup
19
28
 
20
- initializeApiClient({
21
- baseURL: 'http://localhost:8000', // Your backend API URL
22
- onUnauthorized: () => {
23
- // Handle unauthorized (e.g., redirect to login)
24
- window.location.href = '/login';
25
- },
26
- });
27
- ```
29
+ ### 1. Wrap the app in `AuthProvider`
28
30
 
29
- ### 2. Wrap Application with AuthProvider
31
+ `AuthProvider` initializes the API client with the given `apiBaseUrl` and
32
+ manages auth, feature flags, and ally codes for the whole app. Pass the
33
+ **prefixed** backend URL — workspace backends mount their routes under
34
+ `/<service-name>` (see the workspace `CLAUDE.md`).
30
35
 
31
36
  ```tsx
32
37
  import { AuthProvider } from '@psytor/astrogators-shared-ui';
38
+ import '@psytor/astrogators-shared-ui/styles';
33
39
 
34
40
  function App() {
35
41
  return (
36
- <AuthProvider>
37
- {/* Your app content */}
42
+ <AuthProvider apiBaseUrl={import.meta.env.VITE_API_BASE_URL}>
43
+ {/* your app */}
38
44
  </AuthProvider>
39
45
  );
40
46
  }
41
47
  ```
42
48
 
43
- ### 3. Import Global Styles
44
-
45
- ```tsx
46
- import '@psytor/astrogators-shared-ui/styles';
47
- ```
48
-
49
- ## Components
49
+ `VITE_API_BASE_URL` looks like `http://localhost:8000/astrogators-table` in
50
+ dev.
50
51
 
51
- ### Layout Components
52
+ ### 2. (Optional) Reconfigure the API client
52
53
 
53
- #### TopBar
54
+ `AuthProvider` already calls `initializeApiClient`. Call it yourself only if
55
+ you need a custom `onUnauthorized` handler (e.g. router-driven redirects):
54
56
 
55
57
  ```tsx
56
- import { TopBar } from '@psytor/astrogators-shared-ui';
58
+ import { initializeApiClient } from '@psytor/astrogators-shared-ui';
57
59
 
58
- <TopBar
59
- logo={<div>Astrogator's Table</div>}
60
- rightContent={<button>Login</button>}
61
- />
60
+ initializeApiClient({
61
+ baseURL: import.meta.env.VITE_API_BASE_URL,
62
+ onUnauthorized: () => navigate('/login'),
63
+ });
62
64
  ```
63
65
 
64
- #### Container
65
-
66
- ```tsx
67
- import { Container } from '@psytor/astrogators-shared-ui';
66
+ ## Components
68
67
 
69
- <Container maxWidth="lg" padding>
70
- {/* Content */}
71
- </Container>
72
- ```
68
+ All components are styled via CSS Modules and the global design tokens in
69
+ `./styles`. Override the design system by redefining CSS variables on `:root`
70
+ (see "Theming").
73
71
 
74
- #### Footer
72
+ ### Layout
75
73
 
76
74
  ```tsx
77
- import { Footer } from '@psytor/astrogators-shared-ui';
75
+ import { TopBar, Container, Footer } from '@psytor/astrogators-shared-ui';
78
76
 
77
+ <TopBar logo={<Logo />} rightContent={<UserMenu />} />
78
+ <Container maxWidth="lg" padding>{children}</Container>
79
79
  <Footer />
80
80
  ```
81
81
 
82
- ### Form Components
83
-
84
- #### Button
82
+ ### Forms
85
83
 
86
84
  ```tsx
87
- import { Button } from '@psytor/astrogators-shared-ui';
85
+ import { Button, Input, Select, AllyCodeDropdown } from '@psytor/astrogators-shared-ui';
88
86
 
89
- <Button variant="primary" size="md" onClick={handleClick}>
90
- Click Me
91
- </Button>
87
+ <Button variant="primary" size="md" loading={submitting}>Save</Button>
88
+ <Input label="Email" type="email" required error={errors.email} />
89
+ <Select label="Profile" options={profiles} placeholder="Choose…" />
92
90
 
93
- <Button variant="outline" loading>
94
- Loading...
95
- </Button>
91
+ // Wired into useAuth — manages the user's ally codes (DB-backed when
92
+ // authenticated, localStorage when anonymous):
93
+ <AllyCodeDropdown />
96
94
  ```
97
95
 
98
- #### Input
96
+ `Button` variants: `primary | secondary | outline | ghost | danger`.
99
97
 
100
- ```tsx
101
- import { Input } from '@psytor/astrogators-shared-ui';
102
-
103
- <Input
104
- label="Email"
105
- type="email"
106
- placeholder="Enter your email"
107
- required
108
- error={errors.email}
109
- />
110
- ```
111
-
112
- #### Select
98
+ ### Display
113
99
 
114
100
  ```tsx
115
- import { Select } from '@psytor/astrogators-shared-ui';
116
-
117
- <Select
118
- label="Choose Profile"
119
- options={[
120
- { value: 'standard', label: 'Standard' },
121
- { value: 'speed', label: 'Speed Focus' },
122
- ]}
123
- placeholder="Select a profile"
124
- />
125
- ```
126
-
127
- ### Display Components
128
-
129
- #### Card
130
-
131
- ```tsx
132
- import { Card } from '@psytor/astrogators-shared-ui';
101
+ import { Card, Badge, Modal } from '@psytor/astrogators-shared-ui';
133
102
 
134
103
  <Card variant="elevated" chamfered chamferSize="md" padding="lg" hoverable>
135
- <h3>The Mod Ledger</h3>
136
- <p>Analyze your mods</p>
104
+
137
105
  </Card>
138
- ```
139
-
140
- #### Badge
141
-
142
- ```tsx
143
- import { Badge } from '@psytor/astrogators-shared-ui';
144
106
 
145
107
  <Badge variant="success">Active</Badge>
146
108
  <Badge variant="warning" size="sm">Beta</Badge>
147
- ```
148
109
 
149
- #### Modal
150
-
151
- ```tsx
152
- import { Modal } from '@psytor/astrogators-shared-ui';
153
-
154
- <Modal
155
- isOpen={isOpen}
156
- onClose={() => setIsOpen(false)}
157
- title="Login"
158
- size="md"
159
- >
160
- {/* Modal content */}
161
- </Modal>
110
+ <Modal isOpen={open} onClose={close} title="Login" size="md">…</Modal>
162
111
  ```
163
112
 
164
- ### Feedback Components
165
-
166
- #### Loader
113
+ ### Feedback
167
114
 
168
115
  ```tsx
169
116
  import { Loader } from '@psytor/astrogators-shared-ui';
@@ -174,82 +121,96 @@ import { Loader } from '@psytor/astrogators-shared-ui';
174
121
 
175
122
  ## Authentication
176
123
 
177
- ### useAuth Hook
124
+ `useAuth` returns the full auth + ally-code surface. The hook must be called
125
+ inside `AuthProvider`.
178
126
 
179
127
  ```tsx
180
128
  import { useAuth } from '@psytor/astrogators-shared-ui';
181
129
 
182
- function MyComponent() {
183
- const { user, isAuthenticated, login, logout, isLoading } = useAuth();
130
+ const {
131
+ // session
132
+ user, isAuthenticated, isLoading,
133
+ login, register, logout, refreshUser,
134
+ forgotPassword, resetPassword, resendVerification,
184
135
 
185
- const handleLogin = async () => {
186
- try {
187
- await login({
188
- email: 'user@example.com',
189
- password: 'password',
190
- });
191
- } catch (error) {
192
- console.error('Login failed:', error);
193
- }
194
- };
136
+ // backend feature flags (e.g. auth_enabled)
137
+ authEnabled, isLoadingFeatures,
195
138
 
196
- if (isLoading) return <Loader />;
139
+ // ally codes DB-backed when logged in, localStorage when anonymous
140
+ allyCodes, selectedAllyCode, isLoadingAllyCodes,
141
+ fetchAllyCodes, addAllyCode, removeAllyCode,
142
+ selectAllyCode, updateAllyCodeLastUsed,
197
143
 
198
- return (
199
- <div>
200
- {isAuthenticated ? (
201
- <div>
202
- <p>Welcome, {user?.username}!</p>
203
- <button onClick={logout}>Logout</button>
204
- </div>
205
- ) : (
206
- <button onClick={handleLogin}>Login</button>
207
- )}
208
- </div>
209
- );
210
- }
144
+ // localStorage → DB migration prompt for users who sign up after
145
+ // adding ally codes anonymously
146
+ migrationPrompt, dismissMigrationPrompt, migrateLocalStorageCodes,
147
+ } = useAuth();
211
148
  ```
212
149
 
213
- ## API Client
150
+ Tokens are stored in `localStorage`. Registration does **not** auto-login —
151
+ it requires email verification.
214
152
 
215
- ### Using the API Client
153
+ ## API client
216
154
 
217
155
  ```tsx
218
156
  import { apiClient } from '@psytor/astrogators-shared-ui';
219
157
 
220
- // GET request
221
- const data = await apiClient.get('/api/v1/game-data/characters');
222
-
223
- // POST request
158
+ const characters = await apiClient.get('/api/v1/game-data/characters');
224
159
  const result = await apiClient.post('/api/v1/mod-ledger/evaluate/123456789', {
225
160
  profile_name: 'standard',
226
161
  });
227
162
  ```
228
163
 
229
- The API client automatically:
230
- - Injects JWT access token in Authorization header
231
- - Refreshes expired tokens
232
- - Retries failed requests after token refresh
233
- - Calls `onUnauthorized` callback on auth failure
164
+ The client:
165
+ - injects the access token into `Authorization`
166
+ - on `401`, transparently refreshes via `/api/v1/auth/refresh-token` and
167
+ retries the original request once
168
+ - calls `onUnauthorized` if refresh fails
234
169
 
235
- ## TypeScript Types
170
+ Endpoints are written **without** the service prefix — the prefix lives in
171
+ the configured `baseURL`.
236
172
 
237
- All TypeScript types are exported:
173
+ ## Ally code utilities
174
+
175
+ For UI that needs to format/validate the 9-digit SWGOH player IDs outside the
176
+ context of `useAuth`:
177
+
178
+ ```tsx
179
+ import {
180
+ formatAllyCode, // "123456789" → "123-456-789"
181
+ unformatAllyCode, // "123-456-789" → "123456789"
182
+ getAllyCodesFromStorage,
183
+ saveAllyCodeToStorage,
184
+ removeAllyCodeFromStorage,
185
+ getSelectedAllyCode,
186
+ setSelectedAllyCode,
187
+ clearAllyCodes,
188
+ } from '@psytor/astrogators-shared-ui';
189
+ ```
190
+
191
+ Prefer `useAuth` when you can — it keeps DB and localStorage in sync.
192
+
193
+ ## TypeScript
194
+
195
+ All public types are re-exported from the package root, including:
238
196
 
239
197
  ```tsx
240
198
  import type {
241
- User,
242
- LoginRequest,
243
- LoginResponse,
244
- ParsedMod,
245
- ModEvaluation,
246
- ApiError,
199
+ User, LoginRequest, LoginResponse,
200
+ RegisterRequest, ForgotPasswordRequest, ResetPasswordRequest,
201
+ AllyCode, AllyCodeCreate, AllyCodeListResponse, StoredAllyCode,
202
+ ApiResponse, ApiError, PaginatedResponse,
203
+ ParsedMod, ModStat, ModEvaluation, EvaluationRequest, EvaluationResponse,
247
204
  } from '@psytor/astrogators-shared-ui';
248
205
  ```
249
206
 
250
- ## CSS Variables
207
+ Consumers should set `"moduleResolution": "bundler"` (or `"node16"`) in
208
+ `tsconfig.json` so the bundled `.d.ts` files resolve.
209
+
210
+ ## Theming
251
211
 
252
- Customize the design system by overriding CSS variables:
212
+ Design tokens are CSS variables on `:root`. Override anything you need in
213
+ your own stylesheet, loaded after the library styles:
253
214
 
254
215
  ```css
255
216
  :root {
@@ -261,57 +222,32 @@ Customize the design system by overriding CSS variables:
261
222
  }
262
223
  ```
263
224
 
264
- ## Chamfered Boxes
225
+ ### Chamfered boxes
265
226
 
266
- Use the chamfered box effect on any element:
227
+ Sci-fi cut-corner effect, available as utility classes or via `Card`:
267
228
 
268
229
  ```tsx
269
- <div className="chamfered-box">
270
- {/* Content with cut corners */}
271
- </div>
230
+ <div className="chamfered-box">…</div>
231
+ <div className="chamfered-box-sm">…</div>
232
+ <div className="chamfered-box-lg">…</div>
272
233
 
273
- <div className="chamfered-box-lg">
274
- {/* Large chamfered corners */}
275
- </div>
276
-
277
- <div className="chamfered-box-sm">
278
- {/* Small chamfered corners */}
279
- </div>
280
- ```
281
-
282
- Or use the Card component:
283
-
284
- ```tsx
285
- <Card chamfered chamferSize="lg">
286
- {/* Content */}
287
- </Card>
234
+ <Card chamfered chamferSize="lg">…</Card>
288
235
  ```
289
236
 
290
237
  ## Development
291
238
 
292
239
  ```bash
293
- # Install dependencies
294
240
  npm install
295
-
296
- # Build library
297
- npm run build
298
-
299
- # Type check
300
- npm run type-check
241
+ npm run build # tsc && vite build → dist/
242
+ npm run type-check # tsc --noEmit
301
243
  ```
302
244
 
303
- ## Publishing
245
+ There is no `dev` server worth running (this is a library, not an app).
246
+ Iterate by rebuilding and reinstalling in a consumer, or `npm link`.
304
247
 
305
- ```bash
306
- # Bump version
307
- npm version patch # or minor, or major
308
-
309
- # Build
310
- npm run build
311
-
312
- # Publish to GitHub Packages
313
- npm publish
314
- ```
248
+ See `PUBLISHING.md` for the release procedure. The non-negotiable rule:
249
+ **always `npm run build` before `npm publish`** — `dist/` is gitignored but
250
+ is the only thing shipped, so skipping the build re-publishes stale code.
315
251
 
316
252
  ## License
317
253