mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 17:43:47 +00:00
feat: add multi-channel subscription blocking UI and admin management
- Channel subscription blocking screen with per-channel status display - Safe URL handling with protocol validation (no open redirect) - Race-condition-safe subscription check with useRef guard - Admin channel management page with create, toggle, delete - Error handlers on all admin mutations with haptic feedback - Proper type guard for channel subscription error detection - Accessibility: aria-labels on icon-only buttons - Localization: en, ru, zh, fa translations
This commit is contained in:
68
src/api/adminChannels.ts
Normal file
68
src/api/adminChannels.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import apiClient from './client';
|
||||
|
||||
// Types
|
||||
export interface RequiredChannel {
|
||||
id: number;
|
||||
channel_id: string;
|
||||
channel_link: string | null;
|
||||
title: string | null;
|
||||
is_active: boolean;
|
||||
sort_order: number;
|
||||
}
|
||||
|
||||
export interface ChannelListResponse {
|
||||
items: RequiredChannel[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface CreateChannelRequest {
|
||||
channel_id: string;
|
||||
channel_link?: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface UpdateChannelRequest {
|
||||
channel_id?: string;
|
||||
channel_link?: string;
|
||||
title?: string;
|
||||
is_active?: boolean;
|
||||
sort_order?: number;
|
||||
}
|
||||
|
||||
export const adminChannelsApi = {
|
||||
list: async (): Promise<ChannelListResponse> => {
|
||||
const { data } = await apiClient.get<ChannelListResponse>(
|
||||
'/cabinet/admin/channel-subscriptions',
|
||||
);
|
||||
return data;
|
||||
},
|
||||
|
||||
create: async (req: CreateChannelRequest): Promise<RequiredChannel> => {
|
||||
const { data } = await apiClient.post<RequiredChannel>(
|
||||
'/cabinet/admin/channel-subscriptions',
|
||||
req,
|
||||
);
|
||||
return data;
|
||||
},
|
||||
|
||||
update: async (id: number, req: UpdateChannelRequest): Promise<RequiredChannel> => {
|
||||
const { data } = await apiClient.patch<RequiredChannel>(
|
||||
`/cabinet/admin/channel-subscriptions/${id}`,
|
||||
req,
|
||||
);
|
||||
return data;
|
||||
},
|
||||
|
||||
toggle: async (id: number): Promise<RequiredChannel> => {
|
||||
const { data } = await apiClient.post<RequiredChannel>(
|
||||
`/cabinet/admin/channel-subscriptions/${id}/toggle`,
|
||||
);
|
||||
return data;
|
||||
},
|
||||
|
||||
delete: async (id: number): Promise<void> => {
|
||||
await apiClient.delete(`/cabinet/admin/channel-subscriptions/${id}`);
|
||||
},
|
||||
};
|
||||
|
||||
export default adminChannelsApi;
|
||||
@@ -133,6 +133,12 @@ export interface ChannelSubscriptionError {
|
||||
code: 'channel_subscription_required';
|
||||
message: string;
|
||||
channel_link?: string;
|
||||
channels?: Array<{
|
||||
channel_id: string;
|
||||
channel_link?: string;
|
||||
title?: string;
|
||||
is_subscribed: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface BlacklistedError {
|
||||
@@ -189,6 +195,7 @@ apiClient.interceptors.response.use(
|
||||
useBlockingStore.getState().setChannelSubscription({
|
||||
message: detail.message,
|
||||
channel_link: detail.channel_link,
|
||||
channels: detail.channels,
|
||||
});
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ export { infoApi } from './info';
|
||||
export { adminSettingsApi } from './adminSettings';
|
||||
export { adminApi, statsApi } from './admin';
|
||||
export { adminAppsApi } from './adminApps';
|
||||
export { adminChannelsApi } from './adminChannels';
|
||||
export { adminBroadcastsApi } from './adminBroadcasts';
|
||||
export { adminEmailTemplatesApi } from './adminEmailTemplates';
|
||||
export { adminPaymentMethodsApi } from './adminPaymentMethods';
|
||||
|
||||
Reference in New Issue
Block a user