feat: add RowActions component for enhanced row-level actions in data grids
- Introduced RowActions component to manage row-level actions with tooltips and dropdown menus. - Created types for row actions and page actions to standardize action properties. - Implemented utility function to map action intents to Mantine theme colors. - Updated CoreAppShell component to support optional slots for better flexibility. - Added enterprise module structure with context hooks for managing module state and actions. - Implemented draft management for forms to enhance user experience during data entry. - Established context providers for detail, form, and index pages to streamline data handling. - Updated dependencies to ensure compatibility with the latest versions.
This commit is contained in:
@@ -49,10 +49,7 @@ import { DEFAULT_METHODS, DESCRIPTORS, makeDefaultURLs } from './constants';
|
||||
* const { data, status } = await services.getOne<BookingEntity>('42');
|
||||
* ```
|
||||
*/
|
||||
export abstract class BaseRemoteDataServices<
|
||||
E extends BaseEntity = BaseEntity,
|
||||
TDTO = E,
|
||||
> {
|
||||
export abstract class BaseRemoteDataServices<E extends BaseEntity = BaseEntity, TDTO = E> {
|
||||
/** The injected, isolated HTTP client instance. */
|
||||
protected readonly httpClient: AxiosInstance;
|
||||
|
||||
@@ -118,7 +115,7 @@ export abstract class BaseRemoteDataServices<
|
||||
'ex-module-action': action,
|
||||
...(options?.config?.headers ?? {}),
|
||||
},
|
||||
telemetryContext: options?.telemetryContext ?? options?.config?.telemetryContext // FIXME,
|
||||
telemetryContext: options?.telemetryContext ?? options?.config?.telemetryContext, // FIXME,
|
||||
});
|
||||
|
||||
return { data: response.data, status: response.status };
|
||||
@@ -149,9 +146,7 @@ export abstract class BaseRemoteDataServices<
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
async customRequest<T = unknown>(
|
||||
config: AxiosRequestConfig,
|
||||
): Promise<ApiResponse<T>> {
|
||||
async customRequest<T = unknown>(config: AxiosRequestConfig): Promise<ApiResponse<T>> {
|
||||
const response = await this.httpClient.request<T>({
|
||||
...config,
|
||||
headers: {
|
||||
@@ -297,93 +292,63 @@ export abstract class BaseRemoteDataServices<
|
||||
// ─── Data Processing Lifecycle ────────────────────────────────
|
||||
|
||||
/** Confirm processing of a single data record. */
|
||||
confirmProcessData(id: string, config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.confirmProcessData, {
|
||||
confirmData(id: string, config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.confirmData, {
|
||||
variableURL: { id },
|
||||
config,
|
||||
});
|
||||
}
|
||||
|
||||
/** Confirm processing of multiple data records. */
|
||||
batchConfirmProcessData(ids: string[], config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.batchConfirmProcessData, {
|
||||
batchConfirmData(ids: string[], config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.batchConfirmData, {
|
||||
config: { ...config, data: { ids } },
|
||||
});
|
||||
}
|
||||
|
||||
/** Cancel processing of a single data record. */
|
||||
cancelProcessData(id: string, config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.cancelProcessData, {
|
||||
cancelData(id: string, config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.cancelData, {
|
||||
variableURL: { id },
|
||||
config,
|
||||
});
|
||||
}
|
||||
|
||||
/** Cancel processing of multiple data records. */
|
||||
batchCancelProcessData(ids: string[], config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.batchCancelProcessData, {
|
||||
batchCancelData(ids: string[], config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.batchCancelData, {
|
||||
config: { ...config, data: { ids } },
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Transaction Lifecycle ────────────────────────────────────
|
||||
|
||||
/** Confirm a transaction. */
|
||||
confirmProcessTransaction(id: string, config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.confirmProcessTransaction, {
|
||||
variableURL: { id },
|
||||
config,
|
||||
});
|
||||
}
|
||||
|
||||
/** Confirm multiple transactions. */
|
||||
batchConfirmProcessTransaction(ids: string[], config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.batchConfirmProcessTransaction, {
|
||||
config: { ...config, data: { ids } },
|
||||
});
|
||||
}
|
||||
|
||||
/** Cancel a transaction. */
|
||||
cancelProcessTransaction(id: string, config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.cancelProcessTransaction, {
|
||||
variableURL: { id },
|
||||
config,
|
||||
});
|
||||
}
|
||||
|
||||
/** Cancel multiple transactions. */
|
||||
batchCancelProcessTransaction(ids: string[], config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.batchCancelProcessTransaction, {
|
||||
config: { ...config, data: { ids } },
|
||||
});
|
||||
}
|
||||
|
||||
/** Rollback a transaction. */
|
||||
rollbackProcessTransaction(id: string, config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.rollbackProcessTransaction, {
|
||||
rollbackData(id: string, config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.rollbackData, {
|
||||
variableURL: { id },
|
||||
config,
|
||||
});
|
||||
}
|
||||
|
||||
/** Rollback multiple transactions. */
|
||||
batchRollbackProcessTransaction(ids: string[], config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.batchRollbackProcessTransaction, {
|
||||
batchRollbackData(ids: string[], config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.batchRollbackData, {
|
||||
config: { ...config, data: { ids } },
|
||||
});
|
||||
}
|
||||
|
||||
/** Hold a transaction. */
|
||||
holdProcessTransaction(id: string, config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.holdProcessTransaction, {
|
||||
holdData(id: string, config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.holdData, {
|
||||
variableURL: { id },
|
||||
config,
|
||||
});
|
||||
}
|
||||
|
||||
/** Hold multiple transactions. */
|
||||
batchHoldProcessTransaction(ids: string[], config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.batchHoldProcessTransaction, {
|
||||
batchHoldData(ids: string[], config?: AxiosRequestConfig): Promise<ApiResponse<void>> {
|
||||
return this.execute<void>(DESCRIPTORS.batchHoldData, {
|
||||
config: { ...config, data: { ids } },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,10 +11,14 @@ export const REQUEST_ACTION = {
|
||||
CREATE: 'CREATE',
|
||||
EDIT: 'EDIT',
|
||||
DELETE: 'DELETE',
|
||||
CONFIRM_DATA: 'CONFIRM_DATA',
|
||||
CANCEL_DATA: 'CANCEL_DATA',
|
||||
CONFIRM_PROCESS_TRANSACTION: 'CONFIRM_PROCESS_TRANSACTION',
|
||||
CANCEL_PROCESS_TRANSACTION: 'CANCEL_PROCESS_TRANSACTION',
|
||||
|
||||
ACTIVATE: 'ACTIVATE',
|
||||
DEACTIVATE: 'DEACTIVATE',
|
||||
|
||||
CONFIRM: 'CONFIRM',
|
||||
CANCEL: 'CANCEL',
|
||||
ROLLBACK: 'ROLLBACK',
|
||||
HOLD: 'HOLD',
|
||||
} as const;
|
||||
|
||||
// ─── Default HTTP Methods ───────────────────────────────────────
|
||||
@@ -36,19 +40,15 @@ export const DEFAULT_METHODS: RequestMethodMap = {
|
||||
deactivateMethod: 'PATCH',
|
||||
batchDeactivateMethod: 'POST',
|
||||
|
||||
confirmProcessDataMethod: 'PATCH',
|
||||
batchConfirmProcessDataMethod: 'POST',
|
||||
cancelProcessDataMethod: 'PATCH',
|
||||
batchCancelProcessDataMethod: 'POST',
|
||||
confirmDataMethod: 'PATCH',
|
||||
batchConfirmDataMethod: 'POST',
|
||||
cancelDataMethod: 'PATCH',
|
||||
batchCancelDataMethod: 'POST',
|
||||
|
||||
confirmProcessTransactionMethod: 'PATCH',
|
||||
batchConfirmProcessTransactionMethod: 'POST',
|
||||
cancelProcessTransactionMethod: 'PATCH',
|
||||
batchCancelProcessTransactionMethod: 'POST',
|
||||
rollbackProcessTransactionMethod: 'PATCH',
|
||||
batchRollbackProcessTransactionMethod: 'POST',
|
||||
holdProcessTransactionMethod: 'PATCH',
|
||||
batchHoldProcessTransactionMethod: 'POST',
|
||||
rollbackDataMethod: 'PATCH',
|
||||
batchRollbackDataMethod: 'POST',
|
||||
holdDataMethod: 'PATCH',
|
||||
batchHoldDataMethod: 'POST',
|
||||
};
|
||||
|
||||
// ─── Operation Descriptors ──────────────────────────────────────
|
||||
@@ -59,31 +59,63 @@ export const DEFAULT_METHODS: RequestMethodMap = {
|
||||
* and action header — eliminating 22 boilerplate methods.
|
||||
*/
|
||||
export const DESCRIPTORS = {
|
||||
getMany: { urlKey: 'getManyUrl', methodKey: 'getManyMethod', action: REQUEST_ACTION.VIEW },
|
||||
getOne: { urlKey: 'getOneUrl', methodKey: 'getOneMethod', action: REQUEST_ACTION.VIEW },
|
||||
create: { urlKey: 'createUrl', methodKey: 'createMethod', action: REQUEST_ACTION.CREATE },
|
||||
edit: { urlKey: 'editUrl', methodKey: 'editMethod', action: REQUEST_ACTION.EDIT },
|
||||
delete: { urlKey: 'deleteUrl', methodKey: 'deleteMethod', action: REQUEST_ACTION.DELETE },
|
||||
getMany: { urlKey: 'getManyUrl', methodKey: 'getManyMethod', action: REQUEST_ACTION.VIEW },
|
||||
getOne: { urlKey: 'getOneUrl', methodKey: 'getOneMethod', action: REQUEST_ACTION.VIEW },
|
||||
create: { urlKey: 'createUrl', methodKey: 'createMethod', action: REQUEST_ACTION.CREATE },
|
||||
edit: { urlKey: 'editUrl', methodKey: 'editMethod', action: REQUEST_ACTION.EDIT },
|
||||
delete: { urlKey: 'deleteUrl', methodKey: 'deleteMethod', action: REQUEST_ACTION.DELETE },
|
||||
batchDelete: { urlKey: 'batchDeleteUrl', methodKey: 'batchDeleteMethod', action: REQUEST_ACTION.DELETE },
|
||||
|
||||
activate: { urlKey: 'activateUrl', methodKey: 'activateMethod', action: REQUEST_ACTION.CONFIRM_DATA },
|
||||
batchActivate: { urlKey: 'batchActivateUrl', methodKey: 'batchActivateMethod', action: REQUEST_ACTION.CONFIRM_DATA },
|
||||
deactivate: { urlKey: 'deactivateUrl', methodKey: 'deactivateMethod', action: REQUEST_ACTION.CONFIRM_DATA },
|
||||
batchDeactivate: { urlKey: 'batchDeactivateUrl', methodKey: 'batchDeactivateMethod', action: REQUEST_ACTION.CONFIRM_DATA },
|
||||
activate: { urlKey: 'activateUrl', methodKey: 'activateMethod', action: REQUEST_ACTION.ACTIVATE },
|
||||
batchActivate: { urlKey: 'batchActivateUrl', methodKey: 'batchActivateMethod', action: REQUEST_ACTION.ACTIVATE },
|
||||
deactivate: { urlKey: 'deactivateUrl', methodKey: 'deactivateMethod', action: REQUEST_ACTION.DEACTIVATE },
|
||||
batchDeactivate: {
|
||||
urlKey: 'batchDeactivateUrl',
|
||||
methodKey: 'batchDeactivateMethod',
|
||||
action: REQUEST_ACTION.DEACTIVATE,
|
||||
},
|
||||
|
||||
confirmProcessData: { urlKey: 'confirmProcessDataUrl', methodKey: 'confirmProcessDataMethod', action: REQUEST_ACTION.CONFIRM_DATA },
|
||||
batchConfirmProcessData: { urlKey: 'batchConfirmProcessDataUrl', methodKey: 'batchConfirmProcessDataMethod', action: REQUEST_ACTION.CONFIRM_DATA },
|
||||
cancelProcessData: { urlKey: 'cancelProcessDataUrl', methodKey: 'cancelProcessDataMethod', action: REQUEST_ACTION.CANCEL_DATA },
|
||||
batchCancelProcessData: { urlKey: 'batchCancelProcessDataUrl', methodKey: 'batchCancelProcessDataMethod', action: REQUEST_ACTION.CANCEL_DATA },
|
||||
confirmData: {
|
||||
urlKey: 'confirmDataUrl',
|
||||
methodKey: 'confirmDataMethod',
|
||||
action: REQUEST_ACTION.CONFIRM,
|
||||
},
|
||||
batchConfirmData: {
|
||||
urlKey: 'batchConfirmDataUrl',
|
||||
methodKey: 'batchConfirmDataMethod',
|
||||
action: REQUEST_ACTION.CONFIRM,
|
||||
},
|
||||
cancelData: {
|
||||
urlKey: 'cancelDataUrl',
|
||||
methodKey: 'cancelDataMethod',
|
||||
action: REQUEST_ACTION.CANCEL,
|
||||
},
|
||||
batchCancelData: {
|
||||
urlKey: 'batchCancelDataUrl',
|
||||
methodKey: 'batchCancelDataMethod',
|
||||
action: REQUEST_ACTION.CANCEL,
|
||||
},
|
||||
|
||||
confirmProcessTransaction: { urlKey: 'confirmProcessTransactionUrl', methodKey: 'confirmProcessTransactionMethod', action: REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION },
|
||||
batchConfirmProcessTransaction: { urlKey: 'batchConfirmProcessTransactionUrl', methodKey: 'batchConfirmProcessTransactionMethod', action: REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION },
|
||||
cancelProcessTransaction: { urlKey: 'cancelProcessTransactionUrl', methodKey: 'cancelProcessTransactionMethod', action: REQUEST_ACTION.CANCEL_PROCESS_TRANSACTION },
|
||||
batchCancelProcessTransaction: { urlKey: 'batchCancelProcessTransactionUrl', methodKey: 'batchCancelProcessTransactionMethod', action: REQUEST_ACTION.CANCEL_PROCESS_TRANSACTION },
|
||||
rollbackProcessTransaction: { urlKey: 'rollbackProcessTransactionUrl', methodKey: 'rollbackProcessTransactionMethod', action: REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION },
|
||||
batchRollbackProcessTransaction: { urlKey: 'batchRollbackProcessTransactionUrl', methodKey: 'batchRollbackProcessTransactionMethod', action: REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION },
|
||||
holdProcessTransaction: { urlKey: 'holdProcessTransactionUrl', methodKey: 'holdProcessTransactionMethod', action: REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION },
|
||||
batchHoldProcessTransaction: { urlKey: 'batchHoldProcessTransactionUrl', methodKey: 'batchHoldProcessTransactionMethod', action: REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION },
|
||||
rollbackData: {
|
||||
urlKey: 'rollbackDataUrl',
|
||||
methodKey: 'rollbackDataMethod',
|
||||
action: REQUEST_ACTION.ROLLBACK,
|
||||
},
|
||||
batchRollbackData: {
|
||||
urlKey: 'batchRollbackDataUrl',
|
||||
methodKey: 'batchRollbackDataMethod',
|
||||
action: REQUEST_ACTION.ROLLBACK,
|
||||
},
|
||||
holdData: {
|
||||
urlKey: 'holdDataUrl',
|
||||
methodKey: 'holdDataMethod',
|
||||
action: REQUEST_ACTION.HOLD,
|
||||
},
|
||||
batchHoldData: {
|
||||
urlKey: 'batchHoldDataUrl',
|
||||
methodKey: 'batchHoldDataMethod',
|
||||
action: REQUEST_ACTION.HOLD,
|
||||
},
|
||||
} as const satisfies Record<string, RequestDescriptor>;
|
||||
|
||||
// ─── Default URL Factory ────────────────────────────────────────
|
||||
@@ -109,18 +141,14 @@ export function makeDefaultURLs(apiUrl: string): ApiURLMap {
|
||||
deactivateUrl: `${apiUrl}/:id/inactive`,
|
||||
batchDeactivateUrl: `${apiUrl}/batch-inactive`,
|
||||
|
||||
confirmProcessDataUrl: `${apiUrl}/:id/confirm`,
|
||||
batchConfirmProcessDataUrl: `${apiUrl}/batch-confirm`,
|
||||
cancelProcessDataUrl: `${apiUrl}/:id/cancel`,
|
||||
batchCancelProcessDataUrl: `${apiUrl}/batch-cancel`,
|
||||
confirmDataUrl: `${apiUrl}/:id/confirm`,
|
||||
batchConfirmDataUrl: `${apiUrl}/batch-confirm`,
|
||||
cancelDataUrl: `${apiUrl}/:id/cancel`,
|
||||
batchCancelDataUrl: `${apiUrl}/batch-cancel`,
|
||||
|
||||
confirmProcessTransactionUrl: `${apiUrl}/:id/confirm-data`,
|
||||
batchConfirmProcessTransactionUrl: `${apiUrl}/batch-confirm-data`,
|
||||
cancelProcessTransactionUrl: `${apiUrl}/:id/cancel`,
|
||||
batchCancelProcessTransactionUrl: `${apiUrl}/batch-cancel`,
|
||||
rollbackProcessTransactionUrl: `${apiUrl}/:id/confirm-rollback`,
|
||||
batchRollbackProcessTransactionUrl: `${apiUrl}/batch-confirm-rollback`,
|
||||
holdProcessTransactionUrl: `${apiUrl}/:id/confirm-hold`,
|
||||
batchHoldProcessTransactionUrl: `${apiUrl}/batch-confirm-hold`,
|
||||
rollbackDataUrl: `${apiUrl}/:id/rollback`,
|
||||
batchRollbackDataUrl: `${apiUrl}/batch-rollback`,
|
||||
holdDataUrl: `${apiUrl}/:id/hold`,
|
||||
batchHoldDataUrl: `${apiUrl}/batch-hold`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -31,19 +31,15 @@ export interface ApiURLMap {
|
||||
deactivateUrl: string;
|
||||
batchDeactivateUrl: string;
|
||||
|
||||
confirmProcessDataUrl: string;
|
||||
batchConfirmProcessDataUrl: string;
|
||||
cancelProcessDataUrl: string;
|
||||
batchCancelProcessDataUrl: string;
|
||||
confirmDataUrl: string;
|
||||
batchConfirmDataUrl: string;
|
||||
cancelDataUrl: string;
|
||||
batchCancelDataUrl: string;
|
||||
|
||||
confirmProcessTransactionUrl: string;
|
||||
batchConfirmProcessTransactionUrl: string;
|
||||
cancelProcessTransactionUrl: string;
|
||||
batchCancelProcessTransactionUrl: string;
|
||||
rollbackProcessTransactionUrl: string;
|
||||
batchRollbackProcessTransactionUrl: string;
|
||||
holdProcessTransactionUrl: string;
|
||||
batchHoldProcessTransactionUrl: string;
|
||||
rollbackDataUrl: string;
|
||||
batchRollbackDataUrl: string;
|
||||
holdDataUrl: string;
|
||||
batchHoldDataUrl: string;
|
||||
}
|
||||
|
||||
// ─── HTTP Method Map ────────────────────────────────────────────
|
||||
@@ -65,19 +61,15 @@ export interface RequestMethodMap {
|
||||
deactivateMethod: string;
|
||||
batchDeactivateMethod: string;
|
||||
|
||||
confirmProcessDataMethod: string;
|
||||
batchConfirmProcessDataMethod: string;
|
||||
cancelProcessDataMethod: string;
|
||||
batchCancelProcessDataMethod: string;
|
||||
confirmDataMethod: string;
|
||||
batchConfirmDataMethod: string;
|
||||
cancelDataMethod: string;
|
||||
batchCancelDataMethod: string;
|
||||
|
||||
confirmProcessTransactionMethod: string;
|
||||
batchConfirmProcessTransactionMethod: string;
|
||||
cancelProcessTransactionMethod: string;
|
||||
batchCancelProcessTransactionMethod: string;
|
||||
rollbackProcessTransactionMethod: string;
|
||||
batchRollbackProcessTransactionMethod: string;
|
||||
holdProcessTransactionMethod: string;
|
||||
batchHoldProcessTransactionMethod: string;
|
||||
rollbackDataMethod: string;
|
||||
batchRollbackDataMethod: string;
|
||||
holdDataMethod: string;
|
||||
batchHoldDataMethod: string;
|
||||
}
|
||||
|
||||
// ─── Request Descriptor ─────────────────────────────────────────
|
||||
@@ -117,10 +109,7 @@ export interface ExecuteOptions {
|
||||
* @typeParam TEntity - The frontend domain entity type (defaults to BaseEntity)
|
||||
* @typeParam TDTO - The API DTO shape (defaults to TEntity for backward compatibility)
|
||||
*/
|
||||
export interface DataServicesConfig<
|
||||
TEntity extends BaseEntity = BaseEntity,
|
||||
TDTO = TEntity,
|
||||
> {
|
||||
export interface DataServicesConfig<TEntity extends BaseEntity = BaseEntity, TDTO = TEntity> {
|
||||
/** Base API path (e.g., '/bookings'). Used to generate all URL templates. */
|
||||
apiUrl?: string;
|
||||
/** Module key for the 'ex-module-key' header (e.g., 'BOOKING'). */
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
* // => '/orgs/a/users/b'
|
||||
* ```
|
||||
*/
|
||||
export function interpolateUrl(
|
||||
template: string,
|
||||
variables?: Record<string, string>,
|
||||
): string {
|
||||
export function interpolateUrl(template: string, variables?: Record<string, string>): string {
|
||||
if (!variables || Object.keys(variables).length === 0) {
|
||||
return template;
|
||||
}
|
||||
@@ -32,9 +29,7 @@ export function interpolateUrl(
|
||||
const key = segment.slice(1);
|
||||
const value = variables[key];
|
||||
if (value === undefined) {
|
||||
throw new Error(
|
||||
`[interpolateUrl] Missing value for URL parameter ":${key}" in template "${template}"`,
|
||||
);
|
||||
throw new Error(`[interpolateUrl] Missing value for URL parameter ":${key}" in template "${template}"`);
|
||||
}
|
||||
return encodeURIComponent(value);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user