# Admin Workout Editor

Embedded view for creating and managing workouts and exercises. As users interact with the editor, your application will receive events that you can handle to trigger custom logic.

**Available for Flutter and Swift (iOS).**

**Custom Query Options (Flutter):**
| Option | Description |
|--------|-------------|
| hidePlansTab | Hide the plans tab in the dashboard |
| tab | Default tab: "workouts", "exercises", or "plans" |
| isSelectableMenu | Show select button on cards, triggers `_selected` events |

**Available Events:**

*General Events:*
- `kinestex_loaded` - Application fully loaded
- `kinestex_launched` - Successful authentication
- `error_occurred` - Authentication error

*Exercise Events:*
- `exercise_opened` - Exercise detail page opened
- `exercise_selection_opened` - Exercise list page opened
- `exercise_selected` - Exercise selected from menu
- `exercise_saved` - Exercise created/updated
- `exercise_removed` - Exercise removed from workout

*Workout Events:*
- `workout_opened` - Workout detail page opened
- `workout_selection_opened` - Workout list page opened
- `workout_selected` - Workout selected from menu
- `workout_saved` - Workout created/updated

*Plan Events:*
- `plan_opened` - Plan detail page opened
- `plan_selection_opened` - Plan list page opened
- `plan_selected` - Plan selected from menu
- `plan_saved` - Plan created/updated

**Swift Parameters:**
| Parameter | Description |
|-----------|-------------|
| organization (required) | Your organization identifier |
| contentType (optional) | `.workout`, `.plan`, or `.exercise` |
| contentId (optional) | Specific content ID to edit |
| customQueries (optional) | Additional query parameters |

**Admin Workout Editor Integration**

Create the admin workout editor view for managing workouts and exercises:

_Swift (iOS)_
```swift
// Open main admin dashboard
kinestex.createAdminWorkoutEditor(
    organization: "YourOrg",
    isLoading: $isLoading,
    onMessageReceived: { message in
        switch message {
        case .exit_kinestex(_):
            showEditor = false
        default:
            print("Message received: \(message)")
        }
    }
)

// Open specific workout for editing
kinestex.createAdminWorkoutEditor(
    organization: "YourOrg",
    contentType: .workout,
    contentId: "workout123",
    isLoading: $isLoading,
    onMessageReceived: { message in /* handle messages */ }
)

// Open specific exercise for editing
kinestex.createAdminWorkoutEditor(
    organization: "YourOrg",
    contentType: .exercise,
    contentId: "exercise456",
    customQueries: ["language": "en"],
    isLoading: $isLoading,
    onMessageReceived: { message in /* handle messages */ }
)
```

_Flutter_
```dart
KinesteXAIFramework.createAdminWorkoutEditor(
  // Use an organization name to differentiate between different orgs.
  // If you don't plan to use multiple orgs, you can use your company name.
  organization: "your_organization_name",
  isShowKinestex: showKinesteX,
  // OPTIONAL: show/hide content on the admin dashboard
  customQueries: {
    "hidePlansTab": true, // will hide the plans tabs in the dashboard
    "tab": "workouts", // will default to workouts tab. Options: "exercises", "plans"
    "isSelectableMenu": true // show select Button, triggers _selected events
  },
  isLoading: ValueNotifier<bool>(false),
  onMessageReceived: (message) {
    handleWebViewMessage(message);
  },
)
```

**Complete Example**

Full implementation with event handling:

_Swift (iOS)_
```swift
import SwiftUI
import KinesteXAIKit

struct AdminEditorView: View {
    @State private var showEditor = false
    @State private var isLoading = false

    // Initialize KinesteXAIKit with your credentials
    let kinestex = KinesteXAIKit(
        apiKey: "YOUR_API_KEY",
        companyName: "YOUR_COMPANY_NAME",
        userId: "YOUR_USER_ID"
    )

    var body: some View {
        VStack {
            Text("Admin Editor")
                .font(.title)
                .padding()

            Spacer()

            Button(action: {
                showEditor.toggle()
            }) {
                Text("Open Admin Editor")
                    .font(.title3)
                    .foregroundColor(.white)
                    .bold()
                    .padding()
                    .frame(maxWidth: .infinity)
                    .background(Color.blue.cornerRadius(10))
                    .padding(.horizontal)
            }

            Spacer()
        }
        .fullScreenCover(isPresented: $showEditor) {
            kinestex.createAdminWorkoutEditor(
                organization: "YourOrg",
                contentType: nil,
                contentId: nil,
                customQueries: nil,
                isLoading: $isLoading,
                onMessageReceived: { message in
                    switch message {
                    case .exit_kinestex(_):
                        showEditor = false
                    case .error_occurred(let data):
                        print("Error: \(data)")
                    default:
                        print("Message received: \(message)")
                    }
                }
            )
        }
    }
}

#Preview {
    AdminEditorView()
}
```

_Flutter_
```dart
import 'package:flutter/material.dart';
import 'package:kinestex_sdk_flutter/kinestex_sdk.dart';
import 'package:permission_handler/permission_handler.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await KinesteXAIFramework.initialize(
    apiKey: "your_api_key",
    companyName: "your_company_name",
    userId: "your_user_id",
  );
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void dispose() {
    disposeKinesteXAIFramework();
    super.dispose();
  }

  Future<void> disposeKinesteXAIFramework() async {
    await KinesteXAIFramework.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'KinesteX Admin Editor',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  ValueNotifier<bool> showKinesteX = ValueNotifier<bool>(false);

  @override
  void initState() {
    super.initState();
    _checkCameraPermission();
  }

  void _checkCameraPermission() async {
    if (await Permission.camera.request() != PermissionStatus.granted) {
      _showCameraAccessDeniedAlert();
    }
  }

  void _showCameraAccessDeniedAlert() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text("Camera Permission Denied"),
          content: const Text("Camera access is required for this app to function properly."),
          actions: <Widget>[
            TextButton(
              child: const Text("OK"),
              onPressed: () => Navigator.of(context).pop(),
            ),
          ],
        );
      },
    );
  }

  void handleWebViewMessage(WebViewMessage message) {
    if (message is ExitKinestex) {
      setState(() {
        showKinesteX.value = false;
      });
    } else if (message is ErrorOccurred) {
      print('Error: ${message.data["error_message"]}');
    } else {
      // Admin editor events arrive as generic messages — check data['type']
      switch (message.data['type']) {
        case 'workout_saved':
          print('Workout saved: ${message.data["workout_id"]}');
          break;
        case 'exercise_saved':
          print('Exercise saved: ${message.data["exercise_id"]}');
          break;
        case 'workout_selected':
          print('Workout selected: ${message.data["workout_title"]}');
          break;
        case 'exercise_selected':
          print('Exercise selected: ${message.data["exercise_title"]}');
          break;
        case 'plan_saved':
          print('Plan saved: ${message.data["plan_id"]}');
          break;
      }
    }
  }

  Widget createAdminWorkoutEditorView() {
    return Center(
      child: KinesteXAIFramework.createAdminWorkoutEditor(
        organization: "your_organization_name",
        isShowKinestex: showKinesteX,
        customQueries: {
          "hidePlansTab": false,
          "tab": "workouts",
          "isSelectableMenu": true,
        },
        isLoading: ValueNotifier<bool>(false),
        onMessageReceived: handleWebViewMessage,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder(
      valueListenable: showKinesteX,
      builder: (context, isShowKinesteX, child) {
        return isShowKinesteX
            ? SafeArea(
                child: createAdminWorkoutEditorView(),
              )
            : Scaffold(
                body: Center(
                  child: ElevatedButton(
                    style: ElevatedButton.styleFrom(
                      padding: const EdgeInsets.symmetric(
                        horizontal: 40,
                        vertical: 20,
                      ),
                      backgroundColor: Colors.green,
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(10),
                      ),
                    ),
                    onPressed: () {
                      showKinesteX.value = true;
                    },
                    child: const Text(
                      'Open Admin Workout Editor',
                      style: TextStyle(
                        fontSize: 20,
                        fontWeight: FontWeight.bold,
                        color: Colors.white,
                      ),
                    ),
                  ),
                ),
              );
      },
    );
  }
}
```

---
Source: https://www.kinestex.com/docs/integration/admin-workout-editor · Index: https://www.kinestex.com/llms.txt
