@visma-swno/customer-onboarding-wizard 1.0.0 → 1.0.1

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.
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Shared button styles for all wizard step components.
3
+ * Include in `static styles` alongside `designTokens`.
4
+ */
5
+ export declare const buttonStyles: import('lit').CSSResult;
@@ -0,0 +1,98 @@
1
+ import { LitElement } from 'lit';
2
+ import { SupportedLocale } from '../../core/i18n.service';
3
+ export declare class CustomerOnboardingWizard extends LitElement {
4
+ locale: SupportedLocale;
5
+ baseUrl: string;
6
+ adminUrl: string;
7
+ taskId: string;
8
+ customerId: string;
9
+ step: 'welcome' | 'company' | 'administrators' | 'partner-access' | 'complete';
10
+ private static readonly stepMap;
11
+ private userName;
12
+ private currentUserProfile;
13
+ private currentStep;
14
+ private completedSteps;
15
+ private errorState;
16
+ private customerInfo;
17
+ private visible;
18
+ private i18n;
19
+ private httpService;
20
+ private errorMessageListener;
21
+ private get steps();
22
+ connectedCallback(): void;
23
+ disconnectedCallback(): void;
24
+ /**
25
+ * Set up listener for error-message events from HttpService
26
+ * Listens on component element for instance-scoped events
27
+ */
28
+ private setupErrorMessageListener;
29
+ /**
30
+ * Remove error-message listener on component disconnect
31
+ */
32
+ private removeErrorMessageListener;
33
+ /**
34
+ * Dismiss error message manually
35
+ */
36
+ private dismissError;
37
+ /**
38
+ * Initialize component with authentication token from host application
39
+ */
40
+ private initializeWithAuth;
41
+ /**
42
+ * Request authentication token from host application
43
+ * Host must listen to 'request-token' event and provide token via callback
44
+ * @returns Promise that resolves with the authentication token
45
+ */
46
+ private requestAuthToken;
47
+ /**
48
+ * Load user profile from API and set userName
49
+ * userName is populated from getUserProfile() API call
50
+ * Falls back to default 'User' if request fails
51
+ */
52
+ private loadUserProfile;
53
+ /**
54
+ * Load customer info from API
55
+ * customerInfo is populated from getCustomerInfo() API call
56
+ * Falls back to null if request fails or customerId is not provided
57
+ */
58
+ private loadCustomerInfo;
59
+ /**
60
+ * Update authentication token
61
+ * Call this method from host application to update token (e.g., after refresh)
62
+ * @param token - New OAuth2 access token
63
+ */
64
+ updateToken(token: string | null): void;
65
+ willUpdate(changedProperties: Map<string, any>): void;
66
+ static styles: import('lit').CSSResult[];
67
+ /**
68
+ * Load data required for the given step number.
69
+ * Called in two scenarios:
70
+ * 1. On initial load — from `initializeWithAuth()` after the auth token is set,
71
+ * using the `currentStep` derived from the `step` property (direct navigation).
72
+ * 2. On user navigation — from `handleStartSetup()` when entering step 1.
73
+ *
74
+ * When adding data requirements for future steps (e.g. administrators for step 2),
75
+ * add the corresponding fetch call here with the appropriate step guard.
76
+ */
77
+ private loadDataForStep;
78
+ private handleStartSetup;
79
+ private handleNext;
80
+ private handleComplete;
81
+ private handleSaveAndClose;
82
+ private handleEditCustomer;
83
+ private navigate;
84
+ private handleClose;
85
+ private getStepClass;
86
+ private getLineSegmentClass;
87
+ private renderStepIndicators;
88
+ render(): import('lit-html').TemplateResult<1>;
89
+ /**
90
+ * Render error banner if there's an error message
91
+ */
92
+ private renderErrorBanner;
93
+ }
94
+ declare global {
95
+ interface HTMLElementTagNameMap {
96
+ 'vsn-customer-onboarding-wizard': CustomerOnboardingWizard;
97
+ }
98
+ }
@@ -0,0 +1,18 @@
1
+ import { LitElement } from 'lit';
2
+ import { Translations } from '../../core/i18n.service';
3
+ /**
4
+ * Completion Screen Component
5
+ * Displays success message and summary after completing onboarding wizard
6
+ */
7
+ export declare class WizardStepComplete extends LitElement {
8
+ translations: Translations;
9
+ static styles: import('lit').CSSResult[];
10
+ private handleClose;
11
+ private handleGoToAdmin;
12
+ render(): import('lit-html').TemplateResult<1>;
13
+ }
14
+ declare global {
15
+ interface HTMLElementTagNameMap {
16
+ 'wizard-step-complete': WizardStepComplete;
17
+ }
18
+ }
@@ -0,0 +1,19 @@
1
+ import { LitElement } from 'lit';
2
+ import { Translations } from '../../core/i18n.service';
3
+ /**
4
+ * Welcome Step Component
5
+ * Displays welcome message with user name and setup description
6
+ */
7
+ export declare class WizardStepWelcome extends LitElement {
8
+ userName: string;
9
+ translations: Translations;
10
+ static styles: import('lit').CSSResult[];
11
+ private handleStartSetup;
12
+ private handleSaveAndClose;
13
+ render(): import('lit-html').TemplateResult<1>;
14
+ }
15
+ declare global {
16
+ interface HTMLElementTagNameMap {
17
+ 'wizard-step-welcome': WizardStepWelcome;
18
+ }
19
+ }
@@ -0,0 +1,46 @@
1
+ import { LitElement } from 'lit';
2
+ import { Translations, SupportedLocale } from '../../core/i18n.service';
3
+ import { CustomerInfo } from '../../core/http.service';
4
+ /**
5
+ * Company Step Component
6
+ * Displays customer company information including contact, subscriptions, and partner details
7
+ */
8
+ export declare class WizardStep1Company extends LitElement {
9
+ translations: Translations;
10
+ locale: SupportedLocale;
11
+ customerInfo: CustomerInfo | null;
12
+ private editingContact;
13
+ private countryDropdownOpen;
14
+ private fieldErrors;
15
+ private editName;
16
+ private editAddress1;
17
+ private editAddress2;
18
+ private editPostalCode;
19
+ private editCity;
20
+ private editCountryCode;
21
+ private editWebUrl;
22
+ private editContactEmail;
23
+ private localCustomer;
24
+ static styles: import('lit').CSSResult[];
25
+ private getCountryLabel;
26
+ private handleNext;
27
+ private handleSaveAndClose;
28
+ private handleEditContact;
29
+ private handleSaveChanges;
30
+ private handleCancelEdit;
31
+ private validateField;
32
+ private clearFieldError;
33
+ private validateForm;
34
+ private toggleCountryDropdown;
35
+ private selectCountry;
36
+ private handleCountryFocusOut;
37
+ private handleCountryKeydown;
38
+ protected updated(changedProperties: Map<PropertyKey, unknown>): void;
39
+ private handleStepKeydown;
40
+ render(): import('lit-html').TemplateResult<1>;
41
+ }
42
+ declare global {
43
+ interface HTMLElementTagNameMap {
44
+ 'wizard-step1-company': WizardStep1Company;
45
+ }
46
+ }
@@ -0,0 +1,57 @@
1
+ import { LitElement } from 'lit';
2
+ import { Translations, SupportedLocale } from '../../core/i18n.service';
3
+ import { HttpService, UserProfile } from '../../core/http.service';
4
+ /**
5
+ * Administrators Step Component
6
+ * Manages administrator user accounts for the customer
7
+ */
8
+ export declare class WizardStep2Administrators extends LitElement {
9
+ translations: Translations;
10
+ httpService: HttpService | null;
11
+ customerId: string;
12
+ locale: SupportedLocale;
13
+ currentUser: UserProfile | null;
14
+ private existingAdmins;
15
+ private isLoadingAdmins;
16
+ private rows;
17
+ private nextId;
18
+ private lookingUpIds;
19
+ private isConfirming;
20
+ private rowErrors;
21
+ private _loadSequence;
22
+ protected updated(changedProperties: Map<string, unknown>): void;
23
+ private sortAdmins;
24
+ private loadExistingAdmins;
25
+ private get hasOpenRow();
26
+ private get validAdminCount();
27
+ private get canConfirm();
28
+ private get canAddMore();
29
+ private get confirmButtonLabel();
30
+ private addRow;
31
+ private removeRow;
32
+ private doneRow;
33
+ private editRow;
34
+ private updateRowEmail;
35
+ private updateFirstName;
36
+ private updateLastName;
37
+ private setRowFieldError;
38
+ private clearRowFieldError;
39
+ private validateRowEmail;
40
+ private validateRowRequiredField;
41
+ private validateAllRows;
42
+ private lookupUser;
43
+ private handleConfirm;
44
+ private handleSaveAndClose;
45
+ static styles: import('lit').CSSResult[];
46
+ private renderExistingAdminRow;
47
+ private renderLookupRow;
48
+ private renderFoundRow;
49
+ private renderNotFoundRow;
50
+ private renderAdminRow;
51
+ render(): import('lit-html').TemplateResult<1>;
52
+ }
53
+ declare global {
54
+ interface HTMLElementTagNameMap {
55
+ 'wizard-step2-administrators': WizardStep2Administrators;
56
+ }
57
+ }
@@ -0,0 +1,36 @@
1
+ import { LitElement } from 'lit';
2
+ import { Translations } from '../../core/i18n.service';
3
+ import { HttpService } from '../../core/http.service';
4
+ /**
5
+ * Partner Access Step Component
6
+ * Loads consultants from the partner and lets the user select which ones
7
+ * should have access to this customer's account.
8
+ */
9
+ export declare class WizardStep3PartnerAccess extends LitElement {
10
+ translations: Translations;
11
+ httpService: HttpService | null;
12
+ customerId: string;
13
+ partnerId: string;
14
+ partnerName: string;
15
+ private consultants;
16
+ private isLoading;
17
+ private isConfirming;
18
+ private selectedIds;
19
+ private searchTerm;
20
+ private _loadSequence;
21
+ protected updated(changedProperties: Map<string, unknown>): void;
22
+ private loadConsultants;
23
+ private get filteredConsultants();
24
+ private get confirmButtonLabel();
25
+ private toggleConsultant;
26
+ private handleConfirm;
27
+ private handleSaveAndClose;
28
+ private renderConsultantRow;
29
+ static styles: import('lit').CSSResult[];
30
+ render(): import('lit-html').TemplateResult<1>;
31
+ }
32
+ declare global {
33
+ interface HTMLElementTagNameMap {
34
+ 'wizard-step3-partner-access': WizardStep3PartnerAccess;
35
+ }
36
+ }
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Structured HTTP error with a numeric status code.
3
+ * Thrown by HttpService.request() for all non-2xx responses so callers
4
+ * can branch on `error.status` instead of parsing error message strings.
5
+ */
6
+ export declare class HttpError extends Error {
7
+ readonly status: number;
8
+ constructor(status: number, statusText: string);
9
+ }
10
+ export interface RequestConfig {
11
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
12
+ headers?: Record<string, string>;
13
+ body?: any;
14
+ timeout?: number;
15
+ /**
16
+ * HTTP status codes for which the error-message event should NOT be emitted.
17
+ * Use for expected non-success responses (e.g. 404 = user not found).
18
+ */
19
+ suppressErrorStatuses?: number[];
20
+ }
21
+ export interface UserProfile {
22
+ firstName: string;
23
+ lastName: string;
24
+ email: string;
25
+ }
26
+ export interface CustomerInfo {
27
+ customer: {
28
+ customerId: string;
29
+ name: string;
30
+ address1: string;
31
+ address2: string;
32
+ postalCode: string;
33
+ city: string;
34
+ countryCode: string;
35
+ webUrl: string;
36
+ contactEmail: string;
37
+ };
38
+ subscriptions: string[];
39
+ partner: {
40
+ partnerId: string;
41
+ name: string;
42
+ };
43
+ }
44
+ export interface Administrator {
45
+ email: string;
46
+ firstName: string;
47
+ lastName: string;
48
+ }
49
+ export interface AdministratorsResponse {
50
+ content: Administrator[];
51
+ totalNumberOfRecords: number;
52
+ }
53
+ export interface AddAdministratorRequest {
54
+ email: string;
55
+ firstName: string;
56
+ lastName: string;
57
+ languageCode?: string;
58
+ }
59
+ export interface UserByEmailResponse {
60
+ email: string;
61
+ firstName: string;
62
+ lastName: string;
63
+ }
64
+ export interface Consultant {
65
+ id: string;
66
+ email: string;
67
+ firstName: string;
68
+ lastName: string;
69
+ }
70
+ export interface ConsultantsResponse {
71
+ content: Consultant[];
72
+ totalNumberOfRecords: number;
73
+ }
74
+ /**
75
+ * Request body for PUT /api/v1/customers/{customerId}
76
+ * Field names follow the API schema (not the internal CustomerInfo shape)
77
+ */
78
+ export interface EditCustomerRequest {
79
+ name: string;
80
+ address1?: string;
81
+ address2?: string;
82
+ postalCode?: string;
83
+ city?: string;
84
+ countryCode?: string;
85
+ telephone?: string;
86
+ emailAddress?: string;
87
+ webAddress?: string;
88
+ }
89
+ /**
90
+ * HTTP Error event detail
91
+ * Dispatched when authentication/authorization fails (401, 403)
92
+ */
93
+ export interface HttpErrorDetail {
94
+ status: number;
95
+ statusText: string;
96
+ message: string;
97
+ endpoint: string;
98
+ method: string;
99
+ /**
100
+ * Callback to retry the failed request
101
+ * Host application can refresh token, then call retry()
102
+ */
103
+ retry: () => Promise<any>;
104
+ }
105
+ /**
106
+ * Error Message event detail
107
+ * Dispatched for validation errors, server errors, and network errors
108
+ * Component will display these errors internally
109
+ */
110
+ export interface ErrorMessageDetail {
111
+ status: number;
112
+ errorKey: 'invalidRequest' | 'validationError' | 'notFound' | 'serverError' | 'requestTimeout' | 'networkError' | 'requestFailed';
113
+ endpoint: string;
114
+ method: string;
115
+ }
116
+ export declare class HttpService {
117
+ private baseUrl;
118
+ private defaultHeaders;
119
+ private authToken;
120
+ private eventTarget;
121
+ constructor(baseUrl: string, defaultHeaders?: Record<string, string>, eventTarget?: EventTarget);
122
+ /**
123
+ * Set the authentication token for all subsequent requests
124
+ * @param token - OAuth2 access token (from sessionStorage)
125
+ */
126
+ setAuthToken(token: string | null): void;
127
+ /**
128
+ * Get current authentication token
129
+ */
130
+ getAuthToken(): string | null;
131
+ /**
132
+ * Emit HTTP error event for authentication/authorization errors
133
+ * Only emits for 401 (Unauthorized) and 403 (Forbidden)
134
+ * @param status - HTTP status code (401 or 403)
135
+ * @param statusText - HTTP status text
136
+ * @param endpoint - API endpoint that failed
137
+ * @param method - HTTP method
138
+ * @param retryFn - Function to retry the request
139
+ */
140
+ private emitHttpError;
141
+ /**
142
+ * Emit error message event for internal error handling
143
+ * Emitted for validation errors (400, 422), not-found errors (404), server errors (5xx), and network errors
144
+ * Component will display these errors in its UI
145
+ * @param status - HTTP status code or 0 for network errors
146
+ * @param errorKey - Translation key for the emitted error
147
+ * @param endpoint - API endpoint that failed
148
+ * @param method - HTTP method
149
+ */
150
+ private emitErrorMessage;
151
+ request<T>(endpoint: string, config?: RequestConfig): Promise<T>;
152
+ /**
153
+ * Get error key based on status code for i18n translation
154
+ */
155
+ private getErrorKey;
156
+ get<T>(endpoint: string, headers?: Record<string, string>): Promise<T>;
157
+ post<T>(endpoint: string, body: any, headers?: Record<string, string>): Promise<T>;
158
+ put<T>(endpoint: string, body: any, headers?: Record<string, string>): Promise<T>;
159
+ delete<T>(endpoint: string, headers?: Record<string, string>): Promise<T>;
160
+ getUserProfile(): Promise<UserProfile>;
161
+ /**
162
+ * Get customer info for a customer
163
+ * @param customerId - The customer ID
164
+ * @returns Promise with customer, subscriptions, and partner information
165
+ */
166
+ getCustomerInfo(customerId: string): Promise<CustomerInfo>;
167
+ /**
168
+ * Get administrators for a customer
169
+ * @param customerId - The customer ID
170
+ * @returns Promise with paginated list of administrators
171
+ */
172
+ getAdministrators(customerId: string): Promise<AdministratorsResponse>;
173
+ /**
174
+ * Edit customer details (name, address, contact info)
175
+ * PUT /api/v1/customers/{customerId}
176
+ * @param customerId - The customer ID
177
+ * @param data - Fields to update
178
+ * @returns Promise with the parsed response body from the API
179
+ */
180
+ editCustomer(customerId: string, data: EditCustomerRequest): Promise<unknown>;
181
+ /**
182
+ * Save onboarding progress for a task
183
+ * @param taskId - The onboarding task ID
184
+ * @param step - The current step name
185
+ */
186
+ saveProgress(taskId: string, step: 'welcome' | 'company' | 'administrators' | 'partner-access'): Promise<void>;
187
+ /**
188
+ * Delete an onboarding task after completion
189
+ * @param taskId - The onboarding task ID
190
+ */
191
+ deleteTask(taskId: string): Promise<void>;
192
+ /**
193
+ * Look up a user by email address.
194
+ * Returns null when the user does not exist (404), without triggering the
195
+ * error banner — a missing user is a valid business outcome here.
196
+ * @param email - The email address to look up
197
+ */
198
+ getUserByEmail(email: string): Promise<UserByEmailResponse | null>;
199
+ /**
200
+ * Add administrators to a customer account
201
+ * POST /api/v1/onboarding/customers/{customerId}/administrators
202
+ * @param customerId - The customer ID
203
+ * @param admins - Array of administrator data (email, firstName, lastName, languageCode)
204
+ */
205
+ addCustomerAdministrators(customerId: string, admins: AddAdministratorRequest[]): Promise<unknown>;
206
+ /**
207
+ * Get consultants for a partner
208
+ * @param partnerId - The partner ID
209
+ * @returns Promise with paginated list of consultants
210
+ */
211
+ getPartnerConsultants(partnerId: string): Promise<ConsultantsResponse>;
212
+ /**
213
+ * Add consultants to a customer
214
+ * @param customerId - The customer ID
215
+ * @param consultants - Array of consultant data
216
+ */
217
+ addCustomerConsultants(customerId: string, consultants: Consultant[]): Promise<unknown>;
218
+ patch<T>(endpoint: string, body: any, headers?: Record<string, string>): Promise<T>;
219
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * I18n Service
3
+ * Handles internationalization for the customer onboarding wizard
4
+ * Supported locales: da-DK, en-GB, en-US, fi-FI, nb-NO, nl-NL, sv-SE
5
+ */
6
+ export type SupportedLocale = 'da-DK' | 'en-GB' | 'en-US' | 'fi-FI' | 'nb-NO' | 'nl-NL' | 'sv-SE';
7
+ export interface Translations {
8
+ wizard: {
9
+ title: string;
10
+ };
11
+ steps: {
12
+ company: string;
13
+ administrators: string;
14
+ partnerAccess: string;
15
+ progressLabel: string;
16
+ };
17
+ welcome: {
18
+ greeting: string;
19
+ description: string;
20
+ };
21
+ company: {
22
+ title: string;
23
+ description: string;
24
+ placeholder: string;
25
+ contactLabel: string;
26
+ editContactLabel: string;
27
+ subscriptionLabel: string;
28
+ address2Placeholder: string;
29
+ saveChanges: string;
30
+ cancelEdit: string;
31
+ companyNameLabel: string;
32
+ address1Label: string;
33
+ postalCodeLabel: string;
34
+ cityLabel: string;
35
+ countryLabel: string;
36
+ webUrlLabel: string;
37
+ emailLabel: string;
38
+ editFormLabel: string;
39
+ addressGroupLabel: string;
40
+ managePlanMessage: string;
41
+ fieldRequired: string;
42
+ emailInvalid: string;
43
+ };
44
+ administrators: {
45
+ title: string;
46
+ description: string;
47
+ addAdministratorButton: string;
48
+ addNameButton: string;
49
+ firstNameLabel: string;
50
+ lastNameLabel: string;
51
+ youLabel: string;
52
+ deleteRowLabel: string;
53
+ confirmSingular: string;
54
+ confirmPlural: string;
55
+ doneButton: string;
56
+ editButton: string;
57
+ };
58
+ partnerAccess: {
59
+ title: string;
60
+ description: string;
61
+ findConsultantPlaceholder: string;
62
+ loading: string;
63
+ confirmSingular: string;
64
+ confirmPlural: string;
65
+ };
66
+ complete: {
67
+ title: string;
68
+ message1: string;
69
+ message2: string;
70
+ goToAdminButton: string;
71
+ };
72
+ common: {
73
+ saveAndClose: string;
74
+ startSetup: string;
75
+ confirm: string;
76
+ close: string;
77
+ loading: string;
78
+ dismissError: string;
79
+ };
80
+ errors: {
81
+ invalidRequest: {
82
+ title: string;
83
+ message: string;
84
+ };
85
+ validationError: {
86
+ title: string;
87
+ message: string;
88
+ };
89
+ notFound: {
90
+ title: string;
91
+ message: string;
92
+ };
93
+ serverError: {
94
+ title: string;
95
+ message: string;
96
+ };
97
+ requestTimeout: {
98
+ title: string;
99
+ message: string;
100
+ };
101
+ networkError: {
102
+ title: string;
103
+ message: string;
104
+ };
105
+ requestFailed: {
106
+ title: string;
107
+ message: string;
108
+ };
109
+ };
110
+ }
111
+ /**
112
+ * I18n Service Class
113
+ */
114
+ export declare class I18nService {
115
+ private currentLocale;
116
+ /**
117
+ * Set the current locale
118
+ * Returns true when the locale is supported, otherwise falls back to en-GB and returns false.
119
+ */
120
+ setLocale(locale: SupportedLocale | string): boolean;
121
+ /**
122
+ * Get the current locale
123
+ */
124
+ getLocale(): SupportedLocale;
125
+ /**
126
+ * Get translations for the current locale
127
+ */
128
+ getTranslations(): Translations;
129
+ /**
130
+ * Get a translation by key path
131
+ */
132
+ t(key: string): string;
133
+ }
134
+ export declare const i18n: I18nService;
@@ -0,0 +1,21 @@
1
+ import { SVGTemplateResult } from 'lit';
2
+ /**
3
+ * Icon library for the onboarding wizard
4
+ * All icons from Lucide (https://lucide.dev/)
5
+ */
6
+ export declare const icons: {
7
+ circle: () => SVGTemplateResult;
8
+ circleDashed: () => SVGTemplateResult;
9
+ check: () => SVGTemplateResult;
10
+ circleDotDashed: () => SVGTemplateResult;
11
+ circleCheckBig: () => SVGTemplateResult;
12
+ octagonAlert: () => SVGTemplateResult;
13
+ x: () => SVGTemplateResult;
14
+ pencil: () => SVGTemplateResult;
15
+ info: () => SVGTemplateResult;
16
+ chevronDown: () => SVGTemplateResult;
17
+ trash: () => SVGTemplateResult;
18
+ userRound: () => SVGTemplateResult;
19
+ plus: () => SVGTemplateResult;
20
+ search: () => SVGTemplateResult;
21
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Design Tokens
3
+ * Auto-generated from @vsn-ux/gaia-styles/dist/design-tokens.css.
4
+ * Run `npm run generate:tokens` to regenerate after a @vsn-ux/gaia-styles version bump.
5
+ */
6
+ export declare const designTokens: import('lit').CSSResult;
@@ -0,0 +1 @@
1
+ export declare const globalStyles: import('lit').CSSResult;
@@ -0,0 +1,3 @@
1
+ export * from './components/customer-onboarding-wizard/customer-onboarding-wizard';
2
+ export { HttpService } from './core/http.service';
3
+ export type { HttpErrorDetail, ErrorMessageDetail, RequestConfig, UserProfile, CustomerInfo } from './core/http.service';