summit-registration-lite 6.0.8 → 7.0.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.
- package/.claude/rules/summit-registration-lite-component-props.md +95 -0
- package/.claude/rules/summit-registration-lite-payment-providers.md +69 -0
- package/.claude/rules/summit-registration-lite-project.md +80 -0
- package/.claude/rules/summit-registration-lite-redux-actions.md +65 -0
- package/.claude/rules/summit-registration-lite-testing.md +97 -0
- package/.claude/skills/summit-registration-lite-add-provider/SKILL.md +155 -0
- package/.claude/skills/summit-registration-lite-dev-setup/SKILL.md +67 -0
- package/.claude/skills/summit-registration-lite-publish/SKILL.md +64 -0
- package/.claude/skills/summit-registration-lite-scaffold-component/SKILL.md +152 -0
- package/dist/components/index.css +34 -0
- package/dist/components/index.css.map +1 -0
- package/dist/components/index.js +4907 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/login-passwordless.css +2 -1
- package/dist/components/login.css +2 -1
- package/dist/components/registration-form.css +32 -0
- package/dist/components/registration-form.css.map +1 -0
- package/dist/components/registration-form.js +4726 -0
- package/dist/components/registration-form.js.map +1 -0
- package/dist/components/registration-modal.css +34 -0
- package/dist/components/registration-modal.css.map +1 -0
- package/dist/components/registration-modal.js +4870 -0
- package/dist/components/registration-modal.js.map +1 -0
- package/dist/index.css +28 -12
- package/dist/index.css.map +1 -1
- package/dist/index.js +4127 -3892
- package/dist/index.js.map +1 -1
- package/index.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Component Prop Conventions
|
|
2
|
+
|
|
3
|
+
## When to Apply
|
|
4
|
+
|
|
5
|
+
Defining props for new components or understanding prop flow from widget to child components.
|
|
6
|
+
|
|
7
|
+
## Pattern
|
|
8
|
+
|
|
9
|
+
**Widget Root (RegistrationLiteWidget):**
|
|
10
|
+
|
|
11
|
+
Parent app passes all props to widget, which wraps `<RegistrationLite>` in Redux Provider.
|
|
12
|
+
|
|
13
|
+
**Main Component (RegistrationLite):**
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
const RegistrationLite = ({
|
|
17
|
+
// Lifecycle callbacks (parent app control)
|
|
18
|
+
authUser, // (provider) => void - handle SSO login
|
|
19
|
+
goToExtraQuestions, // (attendeeId) => void - redirect to extra questions
|
|
20
|
+
goToEvent, // () => void - redirect to event page
|
|
21
|
+
goToMyOrders, // () => void - redirect to my orders
|
|
22
|
+
onPurchaseComplete, // (order) => void - handle successful purchase
|
|
23
|
+
closeWidget, // () => void - close/minimize widget
|
|
24
|
+
|
|
25
|
+
// Auth callbacks
|
|
26
|
+
getPasswordlessCode, // (email) => void - request OTP code
|
|
27
|
+
loginWithCode, // (email, code) => void - verify OTP
|
|
28
|
+
authErrorCallback, // (error) => void - handle auth errors
|
|
29
|
+
|
|
30
|
+
// Error handling
|
|
31
|
+
onError, // ({type, msg, exception}) => void
|
|
32
|
+
handleCompanyError, // (error) => void - company dropdown errors
|
|
33
|
+
|
|
34
|
+
// Data
|
|
35
|
+
summitData, // { id, name, time_zone, registration_disclaimer, ... }
|
|
36
|
+
profileData, // { email, first_name, last_name, company, ... }
|
|
37
|
+
|
|
38
|
+
// Config
|
|
39
|
+
apiBaseUrl, // Base URL for API calls
|
|
40
|
+
supportEmail, // Support email for help text
|
|
41
|
+
allowPromoCodes, // boolean - show/hide promo code field (default: true)
|
|
42
|
+
showCompanyInput, // boolean - show/hide company field (default: true)
|
|
43
|
+
allowsNativeAuth, // boolean - show email/password login
|
|
44
|
+
allowsOtpAuth, // boolean - show passwordless OTP login
|
|
45
|
+
loginOptions, // [{ button_color, provider_label, provider_param }]
|
|
46
|
+
|
|
47
|
+
// UI customization
|
|
48
|
+
loading, // boolean - external loading state
|
|
49
|
+
showMultipleTicketTexts, // boolean - show multi-ticket messaging
|
|
50
|
+
hidePostalCode, // boolean - hide postal code in payment (default: false)
|
|
51
|
+
idpLogoDark, // string - custom logo src (dark theme)
|
|
52
|
+
idpLogoLight, // string - custom logo src (light theme)
|
|
53
|
+
|
|
54
|
+
// Messages (optional overrides)
|
|
55
|
+
noAllowedTicketsMessage,
|
|
56
|
+
ticketTaxesErrorMessage,
|
|
57
|
+
|
|
58
|
+
// Provider options
|
|
59
|
+
providerOptions, // { stripe: {...}, lawpay: {...} }
|
|
60
|
+
successfulPaymentReturnUrl, // Stripe return URL after payment
|
|
61
|
+
|
|
62
|
+
// Redux state (from connect)
|
|
63
|
+
step, reservation, ticketTypes, taxTypes, ...
|
|
64
|
+
|
|
65
|
+
// Redux actions (from connect)
|
|
66
|
+
loadSession, changeStep, reserveTicket, payTicketWithProvider, ...
|
|
67
|
+
}) => { /* ... */ }
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Key Points
|
|
71
|
+
|
|
72
|
+
- **Callback pattern:** Parent app controls navigation (`goToExtraQuestions`, `goToEvent`, etc.) - widget doesn't redirect directly
|
|
73
|
+
- **Error handling delegation:** Widget calls `onError` / `authErrorCallback` / `handleCompanyError`, parent decides what to do
|
|
74
|
+
- **Data vs. Config:** `summitData` is event info, `profileData` is user info, others are config/behavior flags
|
|
75
|
+
- **Optional overrides:** Most text/messages have defaults but can be overridden via props
|
|
76
|
+
- **Redux separation:** Connected props (`step`, `reservation`) vs. passed props (callbacks, config)
|
|
77
|
+
|
|
78
|
+
## Prop Flow Example
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
Parent App
|
|
82
|
+
└─> RegistrationLiteWidget (wraps Provider)
|
|
83
|
+
└─> RegistrationLite (receives props + Redux state)
|
|
84
|
+
├─> LoginComponent (loginOptions, allowsNativeAuth, allowsOtpAuth)
|
|
85
|
+
├─> TicketTypeComponent (ticketTypes, summitData)
|
|
86
|
+
├─> PersonalInfoComponent (profileData, showCompanyInput)
|
|
87
|
+
└─> PaymentComponent (reservation, providerOptions)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Common Mistakes
|
|
91
|
+
|
|
92
|
+
- Hardcoding navigation instead of calling prop callbacks (breaks widget embedding)
|
|
93
|
+
- Not providing `apiBaseUrl` (causes API calls to fail)
|
|
94
|
+
- Forgetting `onError` handler (errors go to console only, user sees nothing)
|
|
95
|
+
- Passing incomplete `summitData` (missing `time_zone` breaks clock, missing `id` breaks API calls)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Payment Provider Integration
|
|
2
|
+
|
|
3
|
+
## When to Apply
|
|
4
|
+
|
|
5
|
+
Adding or modifying payment providers (Stripe, LawPay, or new providers).
|
|
6
|
+
|
|
7
|
+
## Pattern
|
|
8
|
+
|
|
9
|
+
**Factory Pattern:** `PaymentProviderFactory.build(provider, params)` returns provider instance.
|
|
10
|
+
|
|
11
|
+
**Provider Structure:**
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
export class NewProvider {
|
|
15
|
+
constructor({ reservation, summitId, userProfile, access_token, apiBaseUrl, dispatch }) {
|
|
16
|
+
// Store params as instance properties
|
|
17
|
+
this.reservation = reservation;
|
|
18
|
+
this.summitId = summitId;
|
|
19
|
+
// ... etc
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
payTicket = ({ /* provider-specific params */, onError = () => {} }) => async (dispatch) => {
|
|
23
|
+
// 1. Custom error handler for HTTP status codes
|
|
24
|
+
const errorHandler = (err, res) => (dispatch, state) => {
|
|
25
|
+
switch (err.status) {
|
|
26
|
+
case 404: onError({type: ERROR_TYPE_VALIDATION, msg: res.body.message, exception: null}); break;
|
|
27
|
+
case 500: onError({type: ERROR_TYPE_ERROR, msg: res.body.message, exception: null}); break;
|
|
28
|
+
default: authErrorHandler(err, res)(dispatch, state); break;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// 2. Free/prepaid orders skip payment gateway
|
|
33
|
+
if (isFreeOrder(this.reservation) || isPrePaidOrder(this.reservation)) {
|
|
34
|
+
return putRequest(/* ... checkout endpoint ... */)(params)(this.dispatch);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 3. Payment gateway flow (provider-specific)
|
|
38
|
+
// Call gateway API, handle result, then checkout
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Factory Registration:**
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
// src/utils/payment-providers/payment-provider-factory.js
|
|
47
|
+
import { NEW_PROVIDER } from '../constants';
|
|
48
|
+
import { NewProvider } from './new-provider';
|
|
49
|
+
|
|
50
|
+
case NEW_PROVIDER: {
|
|
51
|
+
currentProvider = new NewProvider({...params});
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Key Points
|
|
57
|
+
|
|
58
|
+
- Free/prepaid orders always skip gateway, go straight to `/checkout` endpoint
|
|
59
|
+
- Error types: `ERROR_TYPE_VALIDATION` (404), `ERROR_TYPE_ERROR` (500), `ERROR_TYPE_PAYMENT` (gateway errors)
|
|
60
|
+
- Always dispatch `startWidgetLoading()` before async ops, `stopWidgetLoading()` after
|
|
61
|
+
- Success flow: checkout API → `CLEAR_RESERVATION` → `changeStep(STEP_COMPLETE)`
|
|
62
|
+
- Failure flow: `removeReservedTicket()` → `changeStep(STEP_PERSONAL_INFO)`
|
|
63
|
+
- Checkout endpoint requires billing address fields from `userProfile`
|
|
64
|
+
|
|
65
|
+
## Common Mistakes
|
|
66
|
+
|
|
67
|
+
- Forgetting to handle free/prepaid orders (they don't have `payment_gateway_client_token`)
|
|
68
|
+
- Not calling `removeReservedTicket()` on payment failure (leaves stale reservation)
|
|
69
|
+
- Missing `stopWidgetLoading()` in catch blocks (spinner never stops)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Project: Summit Registration Lite
|
|
2
|
+
|
|
3
|
+
**Last Updated:** 2026-03-05
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
React widget for summit registration supporting authentication, ticket selection, payment processing, and order completion. Published as NPM package, designed to be embedded in parent applications.
|
|
8
|
+
|
|
9
|
+
## Technology Stack
|
|
10
|
+
|
|
11
|
+
- **Language:** JavaScript (ES6+)
|
|
12
|
+
- **Framework:** React 16+ with Redux for state management
|
|
13
|
+
- **Build Tool:** Webpack 5 (dev/prod configs)
|
|
14
|
+
- **Testing:** Jest with @testing-library/react
|
|
15
|
+
- **Package Manager:** Yarn
|
|
16
|
+
- **Key Libraries:**
|
|
17
|
+
- openstack-uicore-foundation (HTTP utils, actions)
|
|
18
|
+
- redux-persist (state persistence)
|
|
19
|
+
- Stripe.js and custom payment providers
|
|
20
|
+
- Material-UI (@mui/material)
|
|
21
|
+
|
|
22
|
+
## Directory Structure
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
summit-registration-lite/
|
|
26
|
+
├── src/
|
|
27
|
+
│ ├── components/ # React components (login, payment, tickets, etc.)
|
|
28
|
+
│ ├── utils/
|
|
29
|
+
│ │ ├── payment-providers/ # Stripe, LawPay provider implementations
|
|
30
|
+
│ │ ├── hooks/ # Custom React hooks
|
|
31
|
+
│ │ └── constants.js # Step constants, error types
|
|
32
|
+
│ ├── helpers/ # Formatting utilities
|
|
33
|
+
│ ├── actions.js # Redux action creators (API calls)
|
|
34
|
+
│ ├── reducer.js # Redux reducer
|
|
35
|
+
│ ├── store.js # Redux store configuration
|
|
36
|
+
│ └── summit-registration-lite.js # Main widget export
|
|
37
|
+
├── webpack.common.js
|
|
38
|
+
├── webpack.dev.js
|
|
39
|
+
├── webpack.prod.js
|
|
40
|
+
└── babel.config.js
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Key Files
|
|
44
|
+
|
|
45
|
+
- **Entry:** `src/summit-registration-lite.js` - Provider wrapper, exports widget and standalone login components
|
|
46
|
+
- **Main Component:** `src/components/registration-lite.js` - Multi-step registration flow
|
|
47
|
+
- **Actions:** `src/actions.js` - API integration (ticket types, reservations, payments)
|
|
48
|
+
- **Payment:** `src/utils/payment-providers/` - Factory pattern for payment provider abstraction
|
|
49
|
+
- **Config:** `webpack.common.js` - Shared webpack configuration
|
|
50
|
+
|
|
51
|
+
## Development Commands
|
|
52
|
+
|
|
53
|
+
| Task | Command |
|
|
54
|
+
|------|---------|
|
|
55
|
+
| Install | `yarn` |
|
|
56
|
+
| Dev Server | `yarn serve` |
|
|
57
|
+
| Build Dev | `yarn build-dev` |
|
|
58
|
+
| Build Prod | `yarn build` |
|
|
59
|
+
| Test (watch) | `yarn test` |
|
|
60
|
+
| Clean | `yarn clean` |
|
|
61
|
+
| Publish | `yarn publish-package` |
|
|
62
|
+
|
|
63
|
+
## Architecture Notes
|
|
64
|
+
|
|
65
|
+
- **State:** Redux with redux-persist for cross-session state
|
|
66
|
+
- **API Base:** Configurable via props (`apiBaseUrl`), uses openstack-uicore-foundation HTTP utils
|
|
67
|
+
- **Payment Providers:** Factory pattern (`PaymentProviderFactory`) supports Stripe and LawPay
|
|
68
|
+
- **Steps:** Multi-step flow defined in `utils/constants.js` (login, personal info, payment, complete)
|
|
69
|
+
- **Auth:** Supports native auth, OTP (passwordless), and configurable SSO providers via `loginOptions` prop
|
|
70
|
+
- **Integration:** Widget receives callbacks (`authUser`, `goToExtraQuestions`, `onPurchaseComplete`, etc.) for parent app control
|
|
71
|
+
|
|
72
|
+
## Testing
|
|
73
|
+
|
|
74
|
+
- Jest configuration in `package.json`
|
|
75
|
+
- Component tests in `__tests__` directories alongside components
|
|
76
|
+
- Mock files for static assets (`src/__mocks__/fileMock.js`)
|
|
77
|
+
|
|
78
|
+
## Debug Mode
|
|
79
|
+
|
|
80
|
+
URL hash override for testing time-based logic: `#now=2020-06-03,10:59:50` (summit timezone)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Redux API Action Patterns
|
|
2
|
+
|
|
3
|
+
## When to Apply
|
|
4
|
+
|
|
5
|
+
Creating new API actions or modifying existing ones in `src/actions.js`.
|
|
6
|
+
|
|
7
|
+
## Pattern
|
|
8
|
+
|
|
9
|
+
**Action Structure (openstack-uicore-foundation):**
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
export const GET_DATA = 'GET_DATA';
|
|
13
|
+
|
|
14
|
+
export const getData = (summitId) => async (dispatch, getState) => {
|
|
15
|
+
const { apiBaseUrl } = getState().registration;
|
|
16
|
+
|
|
17
|
+
const params = {
|
|
18
|
+
access_token: await accessTokenSelector()(dispatch, getState),
|
|
19
|
+
expand: 'relations,nested.relations'
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return getRequest(
|
|
23
|
+
null,
|
|
24
|
+
createAction(GET_DATA),
|
|
25
|
+
`${apiBaseUrl}/api/v1/summits/${summitId}/resource`,
|
|
26
|
+
customErrorHandler // optional
|
|
27
|
+
)(params)(dispatch);
|
|
28
|
+
};
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Error Handling:**
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
const customErrorHandler = (err, res) => (dispatch, state) => {
|
|
35
|
+
if (err.timeout) return err;
|
|
36
|
+
if (res?.statusCode === 404) return err; // Handle specific codes
|
|
37
|
+
if (res?.statusCode === 500) return err;
|
|
38
|
+
return authErrorHandler(err, res)(dispatch, state); // Default auth handling
|
|
39
|
+
};
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Reducer Convention:**
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
case GET_DATA: {
|
|
46
|
+
return {...state, data: payload.response};
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Key Points
|
|
51
|
+
|
|
52
|
+
- Actions exported as constants (`export const ACTION_NAME`) AND action creators (`export const actionName`)
|
|
53
|
+
- Use `createAction()` from openstack-uicore-foundation for action creators
|
|
54
|
+
- HTTP methods: `getRequest`, `postRequest`, `putRequest`, `deleteRequest`
|
|
55
|
+
- Always pass `params` object with `access_token` (use `accessTokenSelector()` or widget prop)
|
|
56
|
+
- Use `expand` param for nested relations (dot notation: `tickets.owner.extra_questions`)
|
|
57
|
+
- Custom error handlers must return `authErrorHandler` as default case
|
|
58
|
+
- Thunk signature: `(dispatch, getState) => { ... }`
|
|
59
|
+
|
|
60
|
+
## Common Mistakes
|
|
61
|
+
|
|
62
|
+
- Forgetting to await `accessTokenSelector()` (results in Promise instead of token)
|
|
63
|
+
- Not handling 404/500 in custom error handlers (triggers unwanted auth errors)
|
|
64
|
+
- Hardcoding API base URL (always use `state.registration.apiBaseUrl` or prop)
|
|
65
|
+
- Missing `expand` params (causes N+1 queries or missing data in components)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Testing Patterns
|
|
2
|
+
|
|
3
|
+
## When to Apply
|
|
4
|
+
|
|
5
|
+
Writing new component tests or maintaining existing tests.
|
|
6
|
+
|
|
7
|
+
## Pattern
|
|
8
|
+
|
|
9
|
+
**Test Structure:**
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import { render, fireEvent, waitFor, screen } from '@testing-library/react';
|
|
14
|
+
import '@testing-library/jest-dom';
|
|
15
|
+
import ComponentName from '..';
|
|
16
|
+
|
|
17
|
+
const mockCallback = jest.fn();
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
jest.clearAllMocks(); // Reset mocks between tests
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('describes expected behavior', () => {
|
|
24
|
+
const { getByTestId, getAllByTestId, queryByText } = render(
|
|
25
|
+
<ComponentName prop1={value1} callback={mockCallback} />
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// Arrange: find elements
|
|
29
|
+
const button = getByTestId('button-id');
|
|
30
|
+
|
|
31
|
+
// Act: interact
|
|
32
|
+
fireEvent.click(button);
|
|
33
|
+
|
|
34
|
+
// Assert: verify outcome
|
|
35
|
+
expect(mockCallback).toHaveBeenCalledWith(expectedArg);
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Common Queries:**
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
// Single element (throws if not found)
|
|
43
|
+
getByTestId('id') // data-testid attribute
|
|
44
|
+
getByText('text') // visible text content
|
|
45
|
+
getByRole('button') // ARIA role
|
|
46
|
+
|
|
47
|
+
// Multiple elements
|
|
48
|
+
getAllByTestId('id') // returns array
|
|
49
|
+
|
|
50
|
+
// Nullable queries (returns null if not found)
|
|
51
|
+
queryByText('text') // useful for "expect().toBeFalsy()"
|
|
52
|
+
|
|
53
|
+
// From screen utility
|
|
54
|
+
screen.queryByText('text')
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Async Testing:**
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
it('handles async behavior', async () => {
|
|
61
|
+
const { getByTestId } = render(<Component />);
|
|
62
|
+
|
|
63
|
+
fireEvent.click(getByTestId('async-button'));
|
|
64
|
+
|
|
65
|
+
await waitFor(() => {
|
|
66
|
+
expect(getByTestId('result')).toBeInTheDocument();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Key Points
|
|
72
|
+
|
|
73
|
+
- Tests live in `__tests__/` directories alongside components
|
|
74
|
+
- Use `data-testid` attributes for reliable element selection
|
|
75
|
+
- Mock callbacks with `jest.fn()`, verify with `.toHaveBeenCalled()` or `.toHaveBeenCalledWith()`
|
|
76
|
+
- Use `queryBy*` for negative assertions (`expect().toBeFalsy()`)
|
|
77
|
+
- Use `getBy*` for positive assertions (throws if element missing)
|
|
78
|
+
- `@testing-library/react` auto-cleanup enabled (no manual unmount needed)
|
|
79
|
+
- CSS/SCSS mocked via `identity-obj-proxy` (className checks work)
|
|
80
|
+
- Static assets (images) mocked via `src/__mocks__/fileMock.js`
|
|
81
|
+
|
|
82
|
+
## Test Organization
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
src/components/my-component/
|
|
86
|
+
├── index.js
|
|
87
|
+
├── my-component.module.scss
|
|
88
|
+
└── __tests__/
|
|
89
|
+
└── my-component.test.js
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Common Mistakes
|
|
93
|
+
|
|
94
|
+
- Using brittle selectors (text content that changes) instead of `data-testid`
|
|
95
|
+
- Forgetting `@testing-library/jest-dom` import (causes `.toBeInTheDocument()` to fail)
|
|
96
|
+
- Not awaiting async operations (tests pass locally, fail in CI due to timing)
|
|
97
|
+
- Testing implementation details instead of user behavior (prefer user interactions over internal state checks)
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: summit-registration-lite-add-provider
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: Scaffold new payment provider with factory registration
|
|
5
|
+
triggers:
|
|
6
|
+
- add payment provider
|
|
7
|
+
- new payment gateway
|
|
8
|
+
- integrate payment
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Add Payment Provider Workflow
|
|
12
|
+
|
|
13
|
+
Scaffolds a new payment provider class with factory registration, error handling, and test template.
|
|
14
|
+
|
|
15
|
+
## Steps
|
|
16
|
+
|
|
17
|
+
1. **Ask for provider details**
|
|
18
|
+
- Provider name (e.g., "PayPal", "Square")
|
|
19
|
+
- Provider constant (e.g., "PAYMENT_PROVIDER_PAYPAL")
|
|
20
|
+
- Gateway-specific fields needed (API keys, client IDs, etc.)
|
|
21
|
+
|
|
22
|
+
2. **Add constant to utils/constants.js**
|
|
23
|
+
```javascript
|
|
24
|
+
export const PAYMENT_PROVIDER_NEWNAME = 'new_provider_name';
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
3. **Create provider class**
|
|
28
|
+
File: `src/utils/payment-providers/{name}-provider.js`
|
|
29
|
+
|
|
30
|
+
Use StripeProvider as template:
|
|
31
|
+
- Constructor receives: `{ reservation, summitId, userProfile, access_token, apiBaseUrl, dispatch }`
|
|
32
|
+
- Implement `payTicket` method with signature:
|
|
33
|
+
```javascript
|
|
34
|
+
payTicket = ({ /* gateway-specific params */, onError = () => {} }) => async (dispatch) => {
|
|
35
|
+
// Implementation
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
- Handle free/prepaid orders (skip gateway, call `/checkout` directly)
|
|
39
|
+
- Custom error handler for HTTP status codes (404 → VALIDATION, 500 → ERROR)
|
|
40
|
+
- Success flow: checkout → CLEAR_RESERVATION → changeStep(STEP_COMPLETE)
|
|
41
|
+
- Failure flow: removeReservedTicket → changeStep(STEP_PERSONAL_INFO)
|
|
42
|
+
|
|
43
|
+
4. **Register in factory**
|
|
44
|
+
File: `src/utils/payment-providers/payment-provider-factory.js`
|
|
45
|
+
|
|
46
|
+
Add import and case:
|
|
47
|
+
```javascript
|
|
48
|
+
import { NewProvider } from './new-provider';
|
|
49
|
+
|
|
50
|
+
case PAYMENT_PROVIDER_NEWNAME: {
|
|
51
|
+
currentProvider = new NewProvider({...params});
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
5. **Add provider component** (if custom UI needed)
|
|
57
|
+
File: `src/components/{name}-component/index.js`
|
|
58
|
+
|
|
59
|
+
Handles gateway-specific UI (payment form, buttons, etc.).
|
|
60
|
+
|
|
61
|
+
6. **Create test file**
|
|
62
|
+
File: `src/utils/payment-providers/__tests__/{name}-provider.test.js`
|
|
63
|
+
|
|
64
|
+
Mock patterns:
|
|
65
|
+
- Mock API calls (putRequest)
|
|
66
|
+
- Mock gateway SDK (if any)
|
|
67
|
+
- Test free/prepaid flow
|
|
68
|
+
- Test error handling
|
|
69
|
+
|
|
70
|
+
7. **Update PaymentComponent**
|
|
71
|
+
File: `src/components/payment/index.js`
|
|
72
|
+
|
|
73
|
+
Add conditional rendering for new provider UI.
|
|
74
|
+
|
|
75
|
+
## Provider Template
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
import { createAction, putRequest, authErrorHandler } from 'openstack-uicore-foundation/lib/utils/actions';
|
|
79
|
+
import { CLEAR_RESERVATION, PAY_RESERVATION } from '../../actions';
|
|
80
|
+
import { changeStep, removeReservedTicket, startWidgetLoading, stopWidgetLoading } from '../../actions';
|
|
81
|
+
import { isFreeOrder, isPrePaidOrder } from '../utils';
|
|
82
|
+
import { ERROR_TYPE_ERROR, ERROR_TYPE_VALIDATION, STEP_COMPLETE, STEP_PERSONAL_INFO } from '../constants';
|
|
83
|
+
|
|
84
|
+
export class NewProvider {
|
|
85
|
+
constructor({ reservation, summitId, userProfile, access_token, apiBaseUrl, dispatch }) {
|
|
86
|
+
this.reservation = reservation;
|
|
87
|
+
this.summitId = summitId;
|
|
88
|
+
this.userProfile = userProfile;
|
|
89
|
+
this.access_token = access_token;
|
|
90
|
+
this.apiBaseUrl = apiBaseUrl;
|
|
91
|
+
this.dispatch = dispatch;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
payTicket = ({ /* custom params */, onError = () => {} }) => async (dispatch) => {
|
|
95
|
+
const errorHandler = (err, res) => (dispatch, state) => {
|
|
96
|
+
let code = err.status;
|
|
97
|
+
switch (code) {
|
|
98
|
+
case 404: onError({type: ERROR_TYPE_VALIDATION, msg: res.body.message, exception: null}); break;
|
|
99
|
+
case 500: onError({type: ERROR_TYPE_ERROR, msg: res.body.message, exception: null}); break;
|
|
100
|
+
default: authErrorHandler(err, res)(dispatch, state); break;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const normalizedEntity = {
|
|
105
|
+
billing_address_1: this.userProfile?.address1 || '',
|
|
106
|
+
billing_address_2: this.userProfile?.address2 || '',
|
|
107
|
+
billing_address_city: this.userProfile?.locality || '',
|
|
108
|
+
billing_address_state: this.userProfile?.region || '',
|
|
109
|
+
billing_address_country: this.userProfile?.country || ''
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
dispatch(startWidgetLoading());
|
|
113
|
+
|
|
114
|
+
// Free/prepaid orders
|
|
115
|
+
if (isFreeOrder(this.reservation) || isPrePaidOrder(this.reservation)) {
|
|
116
|
+
return this.checkout(normalizedEntity, errorHandler, onError);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Gateway-specific payment flow here
|
|
120
|
+
// Then call this.checkout() on success
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
checkout = (normalizedEntity, errorHandler, onError) => {
|
|
124
|
+
const params = { access_token: this.access_token, expand: 'tickets,tickets.owner,...' };
|
|
125
|
+
|
|
126
|
+
return putRequest(
|
|
127
|
+
null,
|
|
128
|
+
createAction(PAY_RESERVATION),
|
|
129
|
+
`${this.apiBaseUrl}/api/v1/summits/${this.summitId}/orders/${this.reservation.hash}/checkout`,
|
|
130
|
+
normalizedEntity,
|
|
131
|
+
errorHandler
|
|
132
|
+
)(params)(this.dispatch)
|
|
133
|
+
.then((payload) => {
|
|
134
|
+
this.dispatch(stopWidgetLoading());
|
|
135
|
+
this.dispatch(createAction(CLEAR_RESERVATION)({}));
|
|
136
|
+
this.dispatch(changeStep(STEP_COMPLETE));
|
|
137
|
+
return payload;
|
|
138
|
+
})
|
|
139
|
+
.catch(e => {
|
|
140
|
+
this.dispatch(stopWidgetLoading());
|
|
141
|
+
this.dispatch(removeReservedTicket());
|
|
142
|
+
this.dispatch(changeStep(STEP_PERSONAL_INFO));
|
|
143
|
+
onError({type: ERROR_TYPE_ERROR, msg: e?.message, exception: e});
|
|
144
|
+
return e;
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Testing
|
|
151
|
+
|
|
152
|
+
Run provider tests:
|
|
153
|
+
```bash
|
|
154
|
+
yarn test payment-providers
|
|
155
|
+
```
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: summit-registration-lite-dev-setup
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: Clean install and start development server
|
|
5
|
+
triggers:
|
|
6
|
+
- setup dev environment
|
|
7
|
+
- start dev server
|
|
8
|
+
- clean install
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Development Setup Workflow
|
|
12
|
+
|
|
13
|
+
Cleans existing installation and starts fresh development environment.
|
|
14
|
+
|
|
15
|
+
## Steps
|
|
16
|
+
|
|
17
|
+
### Option 1: Clean Setup (Recommended for Issues)
|
|
18
|
+
|
|
19
|
+
1. **Clean all build artifacts and dependencies**
|
|
20
|
+
```bash
|
|
21
|
+
yarn clean
|
|
22
|
+
```
|
|
23
|
+
Removes `dist/` and `node_modules/`, then reinstalls with `yarn`.
|
|
24
|
+
|
|
25
|
+
2. **Start dev server**
|
|
26
|
+
```bash
|
|
27
|
+
yarn serve
|
|
28
|
+
```
|
|
29
|
+
Starts webpack-dev-server on default port (usually 8080). Opens browser automatically.
|
|
30
|
+
|
|
31
|
+
### Option 2: Quick Start (No Clean)
|
|
32
|
+
|
|
33
|
+
1. **Install dependencies** (if `node_modules/` missing)
|
|
34
|
+
```bash
|
|
35
|
+
yarn
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
2. **Start dev server**
|
|
39
|
+
```bash
|
|
40
|
+
yarn serve
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Development Configuration
|
|
44
|
+
|
|
45
|
+
- **Webpack config:** `webpack.dev.js` (extends `webpack.common.js`)
|
|
46
|
+
- **Environment variables:**
|
|
47
|
+
- `API_BASE_URL` - API endpoint for development
|
|
48
|
+
- `ACCESS_TOKEN` - Auth token for standalone testing (stored in localStorage)
|
|
49
|
+
|
|
50
|
+
Set these in shell or `.env` file:
|
|
51
|
+
```bash
|
|
52
|
+
export API_BASE_URL=https://api.dev.example.com
|
|
53
|
+
export ACCESS_TOKEN=your_dev_token
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Standalone Testing
|
|
57
|
+
|
|
58
|
+
The widget supports standalone mode in development. Set environment variables before running `yarn serve` to test without parent app.
|
|
59
|
+
|
|
60
|
+
## Troubleshooting
|
|
61
|
+
|
|
62
|
+
| Issue | Solution |
|
|
63
|
+
|-------|----------|
|
|
64
|
+
| Port already in use | Kill process: `lsof -ti:8080 | xargs kill` or change port in webpack.dev.js |
|
|
65
|
+
| Stale cache | Run `yarn clean` |
|
|
66
|
+
| Module not found | Delete `node_modules/` and `yarn.lock`, then `yarn` |
|
|
67
|
+
| Hot reload not working | Check webpack-dev-server config, ensure `watchFiles` configured |
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: summit-registration-lite-publish
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: Build and publish package to NPM registry
|
|
5
|
+
triggers:
|
|
6
|
+
- publish to npm
|
|
7
|
+
- release package
|
|
8
|
+
- deploy widget
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Package Publishing Workflow
|
|
12
|
+
|
|
13
|
+
Builds the production bundle and publishes to NPM registry.
|
|
14
|
+
|
|
15
|
+
## Steps
|
|
16
|
+
|
|
17
|
+
1. **Verify clean state**
|
|
18
|
+
```bash
|
|
19
|
+
git status
|
|
20
|
+
```
|
|
21
|
+
Ensure no uncommitted changes. If changes exist, ask user to commit or stash first.
|
|
22
|
+
|
|
23
|
+
2. **Verify version**
|
|
24
|
+
```bash
|
|
25
|
+
cat package.json | grep '"version"'
|
|
26
|
+
```
|
|
27
|
+
Confirm version with user before publishing. Ask if version should be bumped (patch/minor/major).
|
|
28
|
+
|
|
29
|
+
3. **Build production bundle**
|
|
30
|
+
```bash
|
|
31
|
+
yarn build
|
|
32
|
+
```
|
|
33
|
+
Compiles with webpack.prod.js. Output goes to `dist/`.
|
|
34
|
+
|
|
35
|
+
4. **Verify build output**
|
|
36
|
+
```bash
|
|
37
|
+
ls -lh dist/
|
|
38
|
+
```
|
|
39
|
+
Check that `index.js` and `index.css` exist.
|
|
40
|
+
|
|
41
|
+
5. **Publish to NPM**
|
|
42
|
+
```bash
|
|
43
|
+
yarn publish
|
|
44
|
+
```
|
|
45
|
+
Prompts for new version (if not already updated). Requires NPM authentication.
|
|
46
|
+
|
|
47
|
+
## Alternative: Combined Command
|
|
48
|
+
|
|
49
|
+
The `yarn publish-package` script combines build and publish:
|
|
50
|
+
```bash
|
|
51
|
+
yarn publish-package
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Prerequisites
|
|
55
|
+
|
|
56
|
+
- NPM authentication configured (`npm login`)
|
|
57
|
+
- Write access to `summit-registration-lite` package on NPM
|
|
58
|
+
- Clean git working directory (or committed changes)
|
|
59
|
+
|
|
60
|
+
## Post-Publish
|
|
61
|
+
|
|
62
|
+
- Tag the release in git: `git tag v{version}`
|
|
63
|
+
- Push tag: `git push origin v{version}`
|
|
64
|
+
- Update CHANGELOG.md with release notes
|