feat: convert ConnectionModal to /connection page with crypto deep links

- Add @kastov/cryptohapp + jsencrypt for encrypted Happ deep links
- Add templateEngine utility to resolve {{SUBSCRIPTION_LINK}},
  {{HAPP_CRYPT3_LINK}}, {{HAPP_CRYPT4_LINK}}, {{USERNAME}} templates
- Convert ConnectionModal component into a standalone Connection page
- Add /connection route with ProtectedRoute and lazy loading
- Replace modal invocation in Dashboard and Subscription with
  navigate('/connection')
- Add type and svgIconKey optional fields to RemnawaveButton
- Show button type badge and SVG icon in AdminApps BlockCard
- Refactor ThemeTab to use local draft state with Save/Cancel flow
This commit is contained in:
c0mrade
2026-02-05 07:33:45 +03:00
parent 35f499673f
commit 445dd0601a
10 changed files with 580 additions and 1214 deletions

View File

@@ -50,91 +50,6 @@ export interface AppConfigResponse {
}
export const adminAppsApi = {
// Get full app config
getConfig: async (): Promise<AppConfigResponse> => {
const response = await apiClient.get<AppConfigResponse>('/cabinet/admin/apps');
return response.data;
},
// Get available platforms
getPlatforms: async (): Promise<string[]> => {
const response = await apiClient.get<string[]>('/cabinet/admin/apps/platforms');
return response.data;
},
// Get apps for a platform
getPlatformApps: async (platform: string): Promise<AppDefinition[]> => {
const response = await apiClient.get<AppDefinition[]>(
`/cabinet/admin/apps/platforms/${platform}`,
);
return response.data;
},
// Create a new app
createApp: async (platform: string, app: AppDefinition): Promise<AppDefinition> => {
const response = await apiClient.post<AppDefinition>(
`/cabinet/admin/apps/platforms/${platform}`,
{
platform,
app,
},
);
return response.data;
},
// Update an app
updateApp: async (
platform: string,
appId: string,
app: AppDefinition,
): Promise<AppDefinition> => {
const response = await apiClient.put<AppDefinition>(
`/cabinet/admin/apps/platforms/${platform}/${appId}`,
{
app,
},
);
return response.data;
},
// Delete an app
deleteApp: async (platform: string, appId: string): Promise<void> => {
await apiClient.delete(`/cabinet/admin/apps/platforms/${platform}/${appId}`);
},
// Reorder apps
reorderApps: async (platform: string, appIds: string[]): Promise<void> => {
await apiClient.post(`/cabinet/admin/apps/platforms/${platform}/reorder`, {
app_ids: appIds,
});
},
// Copy app to another platform
copyApp: async (
platform: string,
appId: string,
targetPlatform: string,
): Promise<{ new_id: string }> => {
const response = await apiClient.post<{ new_id: string; target_platform: string }>(
`/cabinet/admin/apps/platforms/${platform}/copy/${appId}?target_platform=${targetPlatform}`,
);
return response.data;
},
// Get branding
getBranding: async (): Promise<AppConfigBranding> => {
const response = await apiClient.get<AppConfigBranding>('/cabinet/admin/apps/branding');
return response.data;
},
// Update branding
updateBranding: async (branding: AppConfigBranding): Promise<AppConfigBranding> => {
const response = await apiClient.put<AppConfigBranding>('/cabinet/admin/apps/branding', {
branding,
});
return response.data;
},
// Get RemnaWave config status
getRemnaWaveStatus: async (): Promise<{ enabled: boolean; config_uuid: string | null }> => {
const response = await apiClient.get<{ enabled: boolean; config_uuid: string | null }>(
@@ -181,6 +96,8 @@ export const adminAppsApi = {
export interface RemnawaveButton {
url: string;
text: LocalizedText;
type?: 'external' | 'subscriptionLink' | 'copyButton';
svgIconKey?: string;
}
export interface RemnawaveBlock {