import { useState, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { adminAppsApi, AppDefinition, LocalizedText, AppStep, AppButton, exportToRemnawaveFormat, importFromRemnawaveFormat, RemnawaveConfig, importFromRemnawaveFormat as convertRemnawave, } from '../api/adminApps'; import { useBackButton } from '../platform/hooks/useBackButton'; import { usePlatform } from '../platform/hooks/usePlatform'; // Icons const BackIcon = () => ( ); const AppsIcon = () => ( ); const PlusIcon = () => ( ); const EditIcon = () => ( ); const TrashIcon = () => ( ); const ChevronUpIcon = () => ( ); const ChevronDownIcon = () => ( ); const CopyIcon = () => ( ); const StarIcon = ({ filled }: { filled: boolean }) => ( ); const DownloadIcon = () => ( ); const UploadIcon = () => ( ); const CloudSyncIcon = () => ( ); const RefreshIcon = () => ( ); const SettingsIcon = () => ( ); const PLATFORM_LABELS: Record = { ios: 'iOS', android: 'Android', macos: 'macOS', windows: 'Windows', linux: 'Linux', androidTV: 'Android TV', appleTV: 'Apple TV', }; const PLATFORMS = ['ios', 'android', 'macos', 'windows', 'linux', 'androidTV', 'appleTV']; // Helper to create empty localized text const emptyLocalizedText = (): LocalizedText => ({ en: '', ru: '', zh: '', fa: '', }); // Helper to create empty app step const emptyAppStep = (): AppStep => ({ description: emptyLocalizedText(), buttons: [], }); // Helper to create empty app const createEmptyApp = (platform: string): AppDefinition => ({ id: `new-app-${platform}-${Date.now()}`, name: '', isFeatured: false, urlScheme: '', installationStep: emptyAppStep(), addSubscriptionStep: emptyAppStep(), connectAndUseStep: emptyAppStep(), }); interface AppEditorModalProps { app: AppDefinition; platform: string; isNew: boolean; onSave: (app: AppDefinition) => void; onClose: () => void; } function AppEditorModal({ app, platform, isNew, onSave, onClose }: AppEditorModalProps) { const { t } = useTranslation(); const [editedApp, setEditedApp] = useState({ ...app }); const [activeTab, setActiveTab] = useState< 'basic' | 'installation' | 'subscription' | 'connect' | 'additional' >('basic'); const updateField = (field: K, value: AppDefinition[K]) => { setEditedApp((prev) => ({ ...prev, [field]: value })); }; const updateLocalizedText = ( stepKey: | 'installationStep' | 'addSubscriptionStep' | 'connectAndUseStep' | 'additionalBeforeAddSubscriptionStep' | 'additionalAfterAddSubscriptionStep', field: 'description' | 'title', lang: keyof LocalizedText, value: string, ) => { setEditedApp((prev) => { const step = prev[stepKey] || emptyAppStep(); const fieldValue = step[field] || emptyLocalizedText(); return { ...prev, [stepKey]: { ...step, [field]: { ...fieldValue, [lang]: value }, }, }; }); }; const updateButtons = ( stepKey: | 'installationStep' | 'addSubscriptionStep' | 'connectAndUseStep' | 'additionalBeforeAddSubscriptionStep' | 'additionalAfterAddSubscriptionStep', buttons: AppButton[], ) => { setEditedApp((prev) => { const step = prev[stepKey] || emptyAppStep(); return { ...prev, [stepKey]: { ...step, buttons }, }; }); }; const addButton = ( stepKey: | 'installationStep' | 'additionalBeforeAddSubscriptionStep' | 'additionalAfterAddSubscriptionStep', ) => { const step = editedApp[stepKey] || emptyAppStep(); const buttons = step.buttons || []; // Generate unique id for React key const newButton: AppButton = { id: `btn-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`, buttonLink: '', buttonText: emptyLocalizedText(), }; updateButtons(stepKey, [...buttons, newButton]); }; const removeButton = ( stepKey: | 'installationStep' | 'additionalBeforeAddSubscriptionStep' | 'additionalAfterAddSubscriptionStep', index: number, ) => { const step = editedApp[stepKey] || emptyAppStep(); const buttons = step.buttons || []; updateButtons( stepKey, buttons.filter((_, i) => i !== index), ); }; const updateButton = ( stepKey: | 'installationStep' | 'additionalBeforeAddSubscriptionStep' | 'additionalAfterAddSubscriptionStep', index: number, field: 'buttonLink' | 'buttonText', value: string | LocalizedText, ) => { const step = editedApp[stepKey] || emptyAppStep(); const buttons = [...(step.buttons || [])]; buttons[index] = { ...buttons[index], [field]: value }; updateButtons(stepKey, buttons); }; const renderLocalizedTextInputs = ( stepKey: | 'installationStep' | 'addSubscriptionStep' | 'connectAndUseStep' | 'additionalBeforeAddSubscriptionStep' | 'additionalAfterAddSubscriptionStep', field: 'description' | 'title', label: string, ) => { const step = editedApp[stepKey]; const value = step?.[field] || emptyLocalizedText(); return (
{(['en', 'ru'] as const).map((lang) => (
{lang}