@djangocfg/ext-support 1.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.
Files changed (65) hide show
  1. package/README.md +233 -0
  2. package/dist/chunk-AZ4LWZB7.js +2630 -0
  3. package/dist/hooks.cjs +2716 -0
  4. package/dist/hooks.d.cts +255 -0
  5. package/dist/hooks.d.ts +255 -0
  6. package/dist/hooks.js +1 -0
  7. package/dist/index.cjs +2693 -0
  8. package/dist/index.d.cts +1392 -0
  9. package/dist/index.d.ts +1392 -0
  10. package/dist/index.js +1 -0
  11. package/package.json +80 -0
  12. package/src/api/generated/ext_support/_utils/fetchers/ext_support__support.ts +642 -0
  13. package/src/api/generated/ext_support/_utils/fetchers/index.ts +28 -0
  14. package/src/api/generated/ext_support/_utils/hooks/ext_support__support.ts +237 -0
  15. package/src/api/generated/ext_support/_utils/hooks/index.ts +28 -0
  16. package/src/api/generated/ext_support/_utils/schemas/Message.schema.ts +21 -0
  17. package/src/api/generated/ext_support/_utils/schemas/MessageCreate.schema.ts +15 -0
  18. package/src/api/generated/ext_support/_utils/schemas/MessageCreateRequest.schema.ts +15 -0
  19. package/src/api/generated/ext_support/_utils/schemas/MessageRequest.schema.ts +15 -0
  20. package/src/api/generated/ext_support/_utils/schemas/PaginatedMessageList.schema.ts +24 -0
  21. package/src/api/generated/ext_support/_utils/schemas/PaginatedTicketList.schema.ts +24 -0
  22. package/src/api/generated/ext_support/_utils/schemas/PatchedMessageRequest.schema.ts +15 -0
  23. package/src/api/generated/ext_support/_utils/schemas/PatchedTicketRequest.schema.ts +18 -0
  24. package/src/api/generated/ext_support/_utils/schemas/Sender.schema.ts +21 -0
  25. package/src/api/generated/ext_support/_utils/schemas/Ticket.schema.ts +21 -0
  26. package/src/api/generated/ext_support/_utils/schemas/TicketRequest.schema.ts +18 -0
  27. package/src/api/generated/ext_support/_utils/schemas/index.ts +29 -0
  28. package/src/api/generated/ext_support/api-instance.ts +131 -0
  29. package/src/api/generated/ext_support/client.ts +301 -0
  30. package/src/api/generated/ext_support/enums.ts +45 -0
  31. package/src/api/generated/ext_support/errors.ts +116 -0
  32. package/src/api/generated/ext_support/ext_support__support/client.ts +151 -0
  33. package/src/api/generated/ext_support/ext_support__support/index.ts +2 -0
  34. package/src/api/generated/ext_support/ext_support__support/models.ts +165 -0
  35. package/src/api/generated/ext_support/http.ts +103 -0
  36. package/src/api/generated/ext_support/index.ts +273 -0
  37. package/src/api/generated/ext_support/logger.ts +259 -0
  38. package/src/api/generated/ext_support/retry.ts +175 -0
  39. package/src/api/generated/ext_support/schema.json +1049 -0
  40. package/src/api/generated/ext_support/storage.ts +161 -0
  41. package/src/api/generated/ext_support/validation-events.ts +133 -0
  42. package/src/api/index.ts +9 -0
  43. package/src/config.ts +20 -0
  44. package/src/contexts/SupportContext.tsx +250 -0
  45. package/src/contexts/SupportExtensionProvider.tsx +38 -0
  46. package/src/contexts/types.ts +26 -0
  47. package/src/hooks/index.ts +33 -0
  48. package/src/index.ts +39 -0
  49. package/src/layouts/SupportLayout/README.md +91 -0
  50. package/src/layouts/SupportLayout/SupportLayout.tsx +179 -0
  51. package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +155 -0
  52. package/src/layouts/SupportLayout/components/MessageInput.tsx +92 -0
  53. package/src/layouts/SupportLayout/components/MessageList.tsx +312 -0
  54. package/src/layouts/SupportLayout/components/TicketCard.tsx +96 -0
  55. package/src/layouts/SupportLayout/components/TicketList.tsx +153 -0
  56. package/src/layouts/SupportLayout/components/index.ts +6 -0
  57. package/src/layouts/SupportLayout/context/SupportLayoutContext.tsx +258 -0
  58. package/src/layouts/SupportLayout/context/index.ts +2 -0
  59. package/src/layouts/SupportLayout/events.ts +33 -0
  60. package/src/layouts/SupportLayout/hooks/index.ts +2 -0
  61. package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +115 -0
  62. package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +88 -0
  63. package/src/layouts/SupportLayout/index.ts +6 -0
  64. package/src/layouts/SupportLayout/types.ts +21 -0
  65. package/src/utils/logger.ts +14 -0
@@ -0,0 +1,165 @@
1
+ import * as Enums from "../enums";
2
+
3
+ /**
4
+ *
5
+ * Response model (includes read-only fields).
6
+ */
7
+ export interface PaginatedTicketList {
8
+ /** Total number of items across all pages */
9
+ count: number;
10
+ /** Current page number (1-based) */
11
+ page: number;
12
+ /** Total number of pages */
13
+ pages: number;
14
+ /** Number of items per page */
15
+ page_size: number;
16
+ /** Whether there is a next page */
17
+ has_next: boolean;
18
+ /** Whether there is a previous page */
19
+ has_previous: boolean;
20
+ /** Next page number (null if no next page) */
21
+ next_page?: number | null;
22
+ /** Previous page number (null if no previous page) */
23
+ previous_page?: number | null;
24
+ /** Array of items for current page */
25
+ results: Array<Ticket>;
26
+ }
27
+
28
+ /**
29
+ *
30
+ * Request model (no read-only fields).
31
+ */
32
+ export interface TicketRequest {
33
+ user: number;
34
+ subject: string;
35
+ /** * `open` - Open
36
+ * `waiting_for_user` - Waiting for User
37
+ * `waiting_for_admin` - Waiting for Admin
38
+ * `resolved` - Resolved
39
+ * `closed` - Closed */
40
+ status?: Enums.TicketRequestStatus;
41
+ }
42
+
43
+ /**
44
+ *
45
+ * Response model (includes read-only fields).
46
+ */
47
+ export interface Ticket {
48
+ uuid: string;
49
+ user: number;
50
+ subject: string;
51
+ /** * `open` - Open
52
+ * `waiting_for_user` - Waiting for User
53
+ * `waiting_for_admin` - Waiting for Admin
54
+ * `resolved` - Resolved
55
+ * `closed` - Closed */
56
+ status?: Enums.TicketStatus;
57
+ created_at: string;
58
+ /** Get count of unanswered messages for this specific ticket. */
59
+ unanswered_messages_count: number;
60
+ }
61
+
62
+ /**
63
+ *
64
+ * Response model (includes read-only fields).
65
+ */
66
+ export interface PaginatedMessageList {
67
+ /** Total number of items across all pages */
68
+ count: number;
69
+ /** Current page number (1-based) */
70
+ page: number;
71
+ /** Total number of pages */
72
+ pages: number;
73
+ /** Number of items per page */
74
+ page_size: number;
75
+ /** Whether there is a next page */
76
+ has_next: boolean;
77
+ /** Whether there is a previous page */
78
+ has_previous: boolean;
79
+ /** Next page number (null if no next page) */
80
+ next_page?: number | null;
81
+ /** Previous page number (null if no previous page) */
82
+ previous_page?: number | null;
83
+ /** Array of items for current page */
84
+ results: Array<Message>;
85
+ }
86
+
87
+ /**
88
+ *
89
+ * Request model (no read-only fields).
90
+ */
91
+ export interface MessageCreateRequest {
92
+ text: string;
93
+ }
94
+
95
+ /**
96
+ *
97
+ * Response model (includes read-only fields).
98
+ */
99
+ export interface MessageCreate {
100
+ text: string;
101
+ }
102
+
103
+ /**
104
+ *
105
+ * Response model (includes read-only fields).
106
+ */
107
+ export interface Message {
108
+ uuid: string;
109
+ ticket: string;
110
+ sender: Sender;
111
+ /** Check if this message is from the ticket author. */
112
+ is_from_author: boolean;
113
+ text: string;
114
+ created_at: string;
115
+ }
116
+
117
+ /**
118
+ *
119
+ * Request model (no read-only fields).
120
+ */
121
+ export interface MessageRequest {
122
+ text: string;
123
+ }
124
+
125
+ /**
126
+ *
127
+ * Request model (no read-only fields).
128
+ */
129
+ export interface PatchedMessageRequest {
130
+ text?: string;
131
+ }
132
+
133
+ /**
134
+ *
135
+ * Request model (no read-only fields).
136
+ */
137
+ export interface PatchedTicketRequest {
138
+ user?: number;
139
+ subject?: string;
140
+ /** * `open` - Open
141
+ * `waiting_for_user` - Waiting for User
142
+ * `waiting_for_admin` - Waiting for Admin
143
+ * `resolved` - Resolved
144
+ * `closed` - Closed */
145
+ status?: Enums.PatchedTicketRequestStatus;
146
+ }
147
+
148
+ /**
149
+ *
150
+ * Response model (includes read-only fields).
151
+ */
152
+ export interface Sender {
153
+ id: number;
154
+ /** Get formatted username for display. */
155
+ display_username: string;
156
+ email: string;
157
+ avatar?: string | null;
158
+ /** Get user's initials for avatar fallback. */
159
+ initials: string;
160
+ /** Designates whether the user can log into this admin site. */
161
+ is_staff: boolean;
162
+ /** Designates that this user has all permissions without explicitly assigning them. */
163
+ is_superuser: boolean;
164
+ }
165
+
@@ -0,0 +1,103 @@
1
+ /**
2
+ * HTTP Client Adapter Pattern
3
+ *
4
+ * Allows switching between fetch/axios/httpx without changing generated code.
5
+ * Provides unified interface for making HTTP requests.
6
+ */
7
+
8
+ export interface HttpRequest {
9
+ method: string;
10
+ url: string;
11
+ headers?: Record<string, string>;
12
+ body?: any;
13
+ params?: Record<string, any>;
14
+ /** FormData for file uploads (multipart/form-data) */
15
+ formData?: FormData;
16
+ }
17
+
18
+ export interface HttpResponse<T = any> {
19
+ data: T;
20
+ status: number;
21
+ statusText: string;
22
+ headers: Record<string, string>;
23
+ }
24
+
25
+ /**
26
+ * HTTP Client Adapter Interface.
27
+ * Implement this to use custom HTTP clients (axios, httpx, etc.)
28
+ */
29
+ export interface HttpClientAdapter {
30
+ request<T = any>(request: HttpRequest): Promise<HttpResponse<T>>;
31
+ }
32
+
33
+ /**
34
+ * Default Fetch API adapter.
35
+ * Uses native browser fetch() with proper error handling.
36
+ */
37
+ export class FetchAdapter implements HttpClientAdapter {
38
+ async request<T = any>(request: HttpRequest): Promise<HttpResponse<T>> {
39
+ const { method, url, headers, body, params, formData } = request;
40
+
41
+ // Build URL with query params
42
+ let finalUrl = url;
43
+ if (params) {
44
+ const searchParams = new URLSearchParams();
45
+ Object.entries(params).forEach(([key, value]) => {
46
+ if (value !== null && value !== undefined) {
47
+ searchParams.append(key, String(value));
48
+ }
49
+ });
50
+ const queryString = searchParams.toString();
51
+ if (queryString) {
52
+ finalUrl = url.includes('?') ? `${url}&${queryString}` : `${url}?${queryString}`;
53
+ }
54
+ }
55
+
56
+ // Build headers
57
+ const finalHeaders: Record<string, string> = { ...headers };
58
+
59
+ // Determine body and content-type
60
+ let requestBody: string | FormData | undefined;
61
+
62
+ if (formData) {
63
+ // For multipart/form-data, let browser set Content-Type with boundary
64
+ requestBody = formData;
65
+ // Don't set Content-Type - browser will set it with boundary
66
+ } else if (body) {
67
+ // JSON request
68
+ finalHeaders['Content-Type'] = 'application/json';
69
+ requestBody = JSON.stringify(body);
70
+ }
71
+
72
+ // Make request
73
+ const response = await fetch(finalUrl, {
74
+ method,
75
+ headers: finalHeaders,
76
+ body: requestBody,
77
+ credentials: 'include', // Include Django session cookies
78
+ });
79
+
80
+ // Parse response
81
+ let data: any = null;
82
+ const contentType = response.headers.get('content-type');
83
+
84
+ if (response.status !== 204 && contentType?.includes('application/json')) {
85
+ data = await response.json();
86
+ } else if (response.status !== 204) {
87
+ data = await response.text();
88
+ }
89
+
90
+ // Convert Headers to plain object
91
+ const responseHeaders: Record<string, string> = {};
92
+ response.headers.forEach((value, key) => {
93
+ responseHeaders[key] = value;
94
+ });
95
+
96
+ return {
97
+ data,
98
+ status: response.status,
99
+ statusText: response.statusText,
100
+ headers: responseHeaders,
101
+ };
102
+ }
103
+ }
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Django CFG API - API Client with JWT Management
3
+ *
4
+ * Usage:
5
+ * ```typescript
6
+ * import { API } from './api';
7
+ *
8
+ * const api = new API('https://api.example.com');
9
+ *
10
+ * // Set JWT token
11
+ * api.setToken('your-jwt-token', 'refresh-token');
12
+ *
13
+ * // Use API
14
+ * const posts = await api.posts.list();
15
+ * const user = await api.users.retrieve(1);
16
+ *
17
+ * // Check authentication
18
+ * if (api.isAuthenticated()) {
19
+ * // ...
20
+ * }
21
+ *
22
+ * // Custom storage with logging (for Electron/Node.js)
23
+ * import { MemoryStorageAdapter, APILogger } from './storage';
24
+ * const logger = new APILogger({ enabled: true, logLevel: 'debug' });
25
+ * const api = new API('https://api.example.com', {
26
+ * storage: new MemoryStorageAdapter(logger),
27
+ * loggerConfig: { enabled: true, logLevel: 'debug' }
28
+ * });
29
+ *
30
+ * // Get OpenAPI schema
31
+ * const schema = api.getSchema();
32
+ * ```
33
+ */
34
+
35
+ import { APIClient } from "./client";
36
+ import {
37
+ StorageAdapter,
38
+ LocalStorageAdapter,
39
+ CookieStorageAdapter,
40
+ MemoryStorageAdapter
41
+ } from "./storage";
42
+ import type { RetryConfig } from "./retry";
43
+ import type { LoggerConfig } from "./logger";
44
+ import { APILogger } from "./logger";
45
+ import { ExtSupportSupport } from "./ext_support__support/client";
46
+ export * as ExtSupportSupportTypes from "./ext_support__support/models";
47
+ // Note: Direct exports (export * from) are removed to avoid duplicate type conflicts
48
+ // Use namespace exports like CfgAccountsTypes.User or import from specific modules
49
+ export * as Enums from "./enums";
50
+
51
+ // Re-export Zod schemas for runtime validation
52
+ export * as Schemas from "./_utils/schemas";
53
+ // Also export all schemas directly for convenience
54
+ export * from "./_utils/schemas";
55
+
56
+ // Re-export Zod validation events for browser integration
57
+ export type { ValidationErrorDetail, ValidationErrorEvent } from "./validation-events";
58
+ export { dispatchValidationError, onValidationError, formatZodError } from "./validation-events";
59
+
60
+ // Re-export typed fetchers for universal usage
61
+ export * as Fetchers from "./_utils/fetchers";
62
+ export * from "./_utils/fetchers";
63
+
64
+ // Re-export API instance configuration functions
65
+ export {
66
+ configureAPI,
67
+ getAPIInstance,
68
+ reconfigureAPI,
69
+ clearAPITokens,
70
+ resetAPI,
71
+ isAPIConfigured
72
+ } from "./api-instance";
73
+ // NOTE: SWR hooks are generated in ./_utils/hooks/ but NOT exported here to keep
74
+ // the main bundle server-safe. Import hooks directly from the hooks directory:
75
+ // import { useUsers } from './_utils/hooks';
76
+ // Or use a separate entry point like '@djangocfg/api/hooks' for client components.
77
+
78
+ // Re-export core client
79
+ export { APIClient };
80
+
81
+ // Re-export storage adapters for convenience
82
+ export type { StorageAdapter };
83
+ export { LocalStorageAdapter, CookieStorageAdapter, MemoryStorageAdapter };
84
+
85
+ // Re-export error classes for convenience
86
+ export { APIError, NetworkError } from "./errors";
87
+
88
+ // Re-export HTTP adapters for custom implementations
89
+ export type { HttpClientAdapter, HttpRequest, HttpResponse } from "./http";
90
+ export { FetchAdapter } from "./http";
91
+
92
+ // Re-export logger types and classes
93
+ export type { LoggerConfig, RequestLog, ResponseLog, ErrorLog } from "./logger";
94
+ export { APILogger } from "./logger";
95
+
96
+ // Re-export retry configuration and utilities
97
+ export type { RetryConfig, FailedAttemptInfo } from "./retry";
98
+ export { withRetry, shouldRetry, DEFAULT_RETRY_CONFIG } from "./retry";
99
+
100
+ export const TOKEN_KEY = "auth_token";
101
+ export const REFRESH_TOKEN_KEY = "refresh_token";
102
+
103
+ export interface APIOptions {
104
+ /** Custom storage adapter (defaults to LocalStorageAdapter) */
105
+ storage?: StorageAdapter;
106
+ /** Retry configuration for failed requests */
107
+ retryConfig?: RetryConfig;
108
+ /** Logger configuration */
109
+ loggerConfig?: Partial<LoggerConfig>;
110
+ }
111
+
112
+ export class API {
113
+ private baseUrl: string;
114
+ private _client: APIClient;
115
+ private _token: string | null = null;
116
+ private _refreshToken: string | null = null;
117
+ private storage: StorageAdapter;
118
+ private options?: APIOptions;
119
+
120
+ // Sub-clients
121
+ public ext_support_support!: ExtSupportSupport;
122
+
123
+ constructor(baseUrl: string, options?: APIOptions) {
124
+ this.baseUrl = baseUrl;
125
+ this.options = options;
126
+
127
+ // Create logger if config provided
128
+ const logger = options?.loggerConfig ? new APILogger(options.loggerConfig) : undefined;
129
+
130
+ // Initialize storage with logger
131
+ this.storage = options?.storage || new LocalStorageAdapter(logger);
132
+
133
+ this._loadTokensFromStorage();
134
+
135
+ // Initialize APIClient
136
+ this._client = new APIClient(this.baseUrl, {
137
+ retryConfig: this.options?.retryConfig,
138
+ loggerConfig: this.options?.loggerConfig,
139
+ });
140
+
141
+ // Always inject auth header wrapper (reads token dynamically from storage)
142
+ this._injectAuthHeader();
143
+
144
+ // Initialize sub-clients from APIClient
145
+ this.ext_support_support = this._client.ext_support_support;
146
+ }
147
+
148
+ private _loadTokensFromStorage(): void {
149
+ this._token = this.storage.getItem(TOKEN_KEY);
150
+ this._refreshToken = this.storage.getItem(REFRESH_TOKEN_KEY);
151
+ }
152
+
153
+ private _reinitClients(): void {
154
+ this._client = new APIClient(this.baseUrl, {
155
+ retryConfig: this.options?.retryConfig,
156
+ loggerConfig: this.options?.loggerConfig,
157
+ });
158
+
159
+ // Always inject auth header wrapper (reads token dynamically from storage)
160
+ this._injectAuthHeader();
161
+
162
+ // Reinitialize sub-clients
163
+ this.ext_support_support = this._client.ext_support_support;
164
+ }
165
+
166
+ private _injectAuthHeader(): void {
167
+ // Override request method to inject auth header
168
+ const originalRequest = this._client.request.bind(this._client);
169
+ this._client.request = async <T>(
170
+ method: string,
171
+ path: string,
172
+ options?: { params?: Record<string, any>; body?: any; formData?: FormData; headers?: Record<string, string> }
173
+ ): Promise<T> => {
174
+ // Read token from storage dynamically (supports JWT injection after instantiation)
175
+ const token = this.getToken();
176
+ const mergedOptions = {
177
+ ...options,
178
+ headers: {
179
+ ...(options?.headers || {}),
180
+ ...(token ? { 'Authorization': `Bearer ${token}` } : {}),
181
+ },
182
+ };
183
+
184
+ return originalRequest(method, path, mergedOptions);
185
+ };
186
+ }
187
+
188
+ /**
189
+ * Get current JWT token
190
+ */
191
+ getToken(): string | null {
192
+ return this.storage.getItem(TOKEN_KEY);
193
+ }
194
+
195
+ /**
196
+ * Get current refresh token
197
+ */
198
+ getRefreshToken(): string | null {
199
+ return this.storage.getItem(REFRESH_TOKEN_KEY);
200
+ }
201
+
202
+ /**
203
+ * Set JWT token and refresh token
204
+ * @param token - JWT access token
205
+ * @param refreshToken - JWT refresh token (optional)
206
+ */
207
+ setToken(token: string, refreshToken?: string): void {
208
+ this._token = token;
209
+ this.storage.setItem(TOKEN_KEY, token);
210
+
211
+ if (refreshToken) {
212
+ this._refreshToken = refreshToken;
213
+ this.storage.setItem(REFRESH_TOKEN_KEY, refreshToken);
214
+ }
215
+
216
+ // Reinitialize clients with new token
217
+ this._reinitClients();
218
+ }
219
+
220
+ /**
221
+ * Clear all tokens
222
+ */
223
+ clearTokens(): void {
224
+ this._token = null;
225
+ this._refreshToken = null;
226
+ this.storage.removeItem(TOKEN_KEY);
227
+ this.storage.removeItem(REFRESH_TOKEN_KEY);
228
+
229
+ // Reinitialize clients without token
230
+ this._reinitClients();
231
+ }
232
+
233
+ /**
234
+ * Check if user is authenticated
235
+ */
236
+ isAuthenticated(): boolean {
237
+ return !!this.getToken();
238
+ }
239
+
240
+ /**
241
+ * Update base URL and reinitialize clients
242
+ * @param url - New base URL
243
+ */
244
+ setBaseUrl(url: string): void {
245
+ this.baseUrl = url;
246
+ this._reinitClients();
247
+ }
248
+
249
+ /**
250
+ * Get current base URL
251
+ */
252
+ getBaseUrl(): string {
253
+ return this.baseUrl;
254
+ }
255
+
256
+ /**
257
+ * Get OpenAPI schema path
258
+ * @returns Path to the OpenAPI schema JSON file
259
+ *
260
+ * Note: The OpenAPI schema is available in the schema.json file.
261
+ * You can load it dynamically using:
262
+ * ```typescript
263
+ * const schema = await fetch('./schema.json').then(r => r.json());
264
+ * // or using fs in Node.js:
265
+ * // const schema = JSON.parse(fs.readFileSync('./schema.json', 'utf-8'));
266
+ * ```
267
+ */
268
+ getSchemaPath(): string {
269
+ return './schema.json';
270
+ }
271
+ }
272
+
273
+ export default API;