From c6035b97d9c0abdaa6a04a1962bec558b7ae07ea Mon Sep 17 00:00:00 2001 From: chahinebrini Date: Thu, 18 Jun 2026 07:25:43 +0200 Subject: [PATCH] feat(magic): inline enrollment in device card, remove preflight, align flag order --- .sixth/skills/ui-ux-pro-max | 1 + .../app/components/IosDeviceCard.vue | 211 ++- .../app/components/PreflightItem.vue | 43 - apps/rebreak-magic/app/pages/detect.vue | 2 +- apps/rebreak-magic/app/pages/preflight.vue | 79 - apps/rebreak-magic/app/pages/supervise.vue | 2 +- graphify-out/GRAPH_REPORT.md | 12 +- graphify-out/graph.json | 1582 ++++++++--------- graphify-out/manifest.json | 31 +- 9 files changed, 1014 insertions(+), 949 deletions(-) create mode 160000 .sixth/skills/ui-ux-pro-max delete mode 100644 apps/rebreak-magic/app/components/PreflightItem.vue delete mode 100644 apps/rebreak-magic/app/pages/preflight.vue diff --git a/.sixth/skills/ui-ux-pro-max b/.sixth/skills/ui-ux-pro-max new file mode 160000 index 0000000..b7e3af8 --- /dev/null +++ b/.sixth/skills/ui-ux-pro-max @@ -0,0 +1 @@ +Subproject commit b7e3af80f6e331f6fb456667b82b12cade7c9d35 diff --git a/apps/rebreak-magic/app/components/IosDeviceCard.vue b/apps/rebreak-magic/app/components/IosDeviceCard.vue index 5b3c641..5b0f29f 100644 --- a/apps/rebreak-magic/app/components/IosDeviceCard.vue +++ b/apps/rebreak-magic/app/components/IosDeviceCard.vue @@ -177,6 +177,89 @@ + +
+
+ +

+ MDM-Enrollment +

+
+ + +
+ + 1. Profil laden + + + + 2. QR-Code scannen + + + + 3. Prüfen + +
+ + +
+
+ Enrollment QR-Code +
+

+ Scanne den Code mit der iPhone-Kamera und installiere das Profil. +

+ + Installation prüfen + +
+ + +
+ ✓ Enrollment abgeschlossen. Das Gerät synchronisiert sich jetzt mit dem Backend. +
+
+ ✗ {{ enrollmentError || "Enrollment fehlgeschlagen" }} +
+ + +
+
{{ enrollmentLogs.join('\n') }}
+
+ +
+ + Schließen + +
+
+
import { computed, onMounted, ref, watch } from "vue"; +import QRCode from "qrcode"; import type { ComputedDevice, DeviceStatus } from "~/composables/useDeviceStatus"; import { useMdmStatus } from "~/composables/useMdmStatus"; -import { REBREAK_MDM_VERSION, getInstalledMdmVersion, type IphoneDeviceState } from "~/composables/useTauri"; +import { useTauri, REBREAK_MDM_VERSION, getInstalledMdmVersion, type IphoneDeviceState, type LocalServerInfo } from "~/composables/useTauri"; const props = defineProps<{ device: ComputedDevice; @@ -248,6 +332,20 @@ const manualSyncing = ref(false); const autoSyncing = ref(false); const autoSyncComplete = ref(false); +const { + downloadAndPatchEnrollmentProfile, + startLocalProfileServer, + stopLocalProfileServer, + getInstalledProfiles, + mdmPush, +} = useTauri(); + +const enrollmentPhase = ref<"idle" | "loading" | "waiting" | "checking" | "success" | "error">("idle"); +const enrollmentServerInfo = ref(null); +const enrollmentQrUrl = ref(""); +const enrollmentError = ref(null); +const enrollmentLogs = ref([]); + const localEnrollment = computed(() => props.iphone?.installedProfileIDs?.includes(ENROLLMENT_PROFILE_ID) ?? false, ); @@ -314,16 +412,16 @@ const localRows = computed(() => { const iphone = props.iphone; return [ { - label: "Supervised", - value: iphone.isSupervised ? "Ja" : "Nein", - valueClass: iphone.isSupervised + label: "Enrollment", + value: localEnrollment.value ? "Ja" : "Nein", + valueClass: localEnrollment.value ? "text-green-600 dark:text-green-400 font-medium" : "text-red-600 dark:text-red-400 font-medium", }, { - label: "Enrollment", - value: localEnrollment.value ? "Ja" : "Nein", - valueClass: localEnrollment.value + label: "Supervised", + value: iphone.isSupervised ? "Ja" : "Nein", + valueClass: iphone.isSupervised ? "text-green-600 dark:text-green-400 font-medium" : "text-red-600 dark:text-red-400 font-medium", }, @@ -479,6 +577,15 @@ const action = computed(() => { }; } + if (enrollmentPhase.value !== "idle") { + return { + label: "Enrollment läuft…", + icon: "i-heroicons-arrow-path", + color: "neutral", + variant: "soft", + }; + } + if (!props.isConnected || !props.iphone) { return { label: "iPhone verbinden, um ReBreak Cloud zu synchronisieren", @@ -512,12 +619,20 @@ const action = computed(() => { if (!backend?.enrolled || !localEnrollment.value) { const isKnownDevice = !!props.device.mdmId; + if (isKnownDevice) { + return { + label: "Enrollment starten", + icon: "i-heroicons-document-check", + color: "primary", + variant: "solid", + }; + } return { - label: isKnownDevice ? "Schutz vervollständigen" : "Enrollen", - icon: isKnownDevice ? "i-heroicons-shield-check" : "i-heroicons-document-check", + label: "Enrollen", + icon: "i-heroicons-document-check", color: "primary", variant: "solid", - to: isKnownDevice ? "/preflight" : "/enroll", + to: "/enroll", }; } @@ -627,10 +742,86 @@ function onActionClick() { return; } + const backend = mdmState.value.data; + if (props.device.mdmId && (!backend?.enrolled || !localEnrollment.value)) { + startInlineEnrollment(); + return; + } + manualSyncing.value = true; emit("sync", props.device); setTimeout(() => { manualSyncing.value = false; }, 800); } + +async function startInlineEnrollment() { + if (!props.iphone?.udid) return; + + enrollmentPhase.value = "loading"; + enrollmentError.value = null; + enrollmentLogs.value = []; + enrollmentQrUrl.value = ""; + enrollmentServerInfo.value = null; + + try { + const url = "https://mdm.rebreak.org/enrollment/rebreak-enrollment.mobileconfig"; + enrollmentLogs.value.push(`→ Lade Enrollment-Profil`); + const path = await downloadAndPatchEnrollmentProfile(url, props.iphone.udid); + enrollmentLogs.value.push(`✓ Profil gespeichert`); + + enrollmentServerInfo.value = await startLocalProfileServer(path); + enrollmentLogs.value.push(`✓ Lokaler Server gestartet`); + + enrollmentQrUrl.value = await QRCode.toDataURL(enrollmentServerInfo.value.qr_payload, { + width: 192, + margin: 2, + }); + + enrollmentPhase.value = "waiting"; + } catch (e: any) { + enrollmentError.value = e?.message ?? "Enrollment konnte nicht gestartet werden"; + enrollmentLogs.value.push(`✗ ${enrollmentError.value}`); + enrollmentPhase.value = "error"; + } +} + +async function checkInlineEnrollment() { + if (!props.iphone?.udid) return; + + enrollmentPhase.value = "checking"; + enrollmentError.value = null; + + try { + const ids = await getInstalledProfiles(); + if (props.iphone) { + props.iphone.installedProfileIDs = ids; + } + + if (!ids.includes(ENROLLMENT_PROFILE_ID)) { + enrollmentError.value = "Enrollment-Profil noch nicht installiert. Bitte QR-Code scannen und Profil installieren."; + enrollmentPhase.value = "error"; + return; + } + + enrollmentLogs.value.push("✓ Enrollment-Profil erkannt"); + const push = await mdmPush(props.iphone.udid); + enrollmentLogs.value.push(`✓ Push: ${push.push_result}`); + + await refreshMdmStatus(); + enrollmentPhase.value = "success"; + } catch (e: any) { + enrollmentError.value = e?.message ?? "Prüfung fehlgeschlagen"; + enrollmentLogs.value.push(`✗ ${enrollmentError.value}`); + enrollmentPhase.value = "error"; + } +} + +function closeInlineEnrollment() { + stopLocalProfileServer(); + enrollmentPhase.value = "idle"; + enrollmentServerInfo.value = null; + enrollmentQrUrl.value = ""; + enrollmentError.value = null; +} diff --git a/apps/rebreak-magic/app/components/PreflightItem.vue b/apps/rebreak-magic/app/components/PreflightItem.vue deleted file mode 100644 index a29420e..0000000 --- a/apps/rebreak-magic/app/components/PreflightItem.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - diff --git a/apps/rebreak-magic/app/pages/detect.vue b/apps/rebreak-magic/app/pages/detect.vue index 3d55cce..77f86d0 100644 --- a/apps/rebreak-magic/app/pages/detect.vue +++ b/apps/rebreak-magic/app/pages/detect.vue @@ -97,7 +97,7 @@ Zurück -
-
-
-

Pre-Flight Check

-

- Bevor wir dein iPhone supervisieren, müssen ein paar Apple-Sicherheitschecks erledigt sein. -

-
- - -
- - - - -
-
- -
- - Zurück - - - Supervisieren starten → - -
-
-
- - - diff --git a/apps/rebreak-magic/app/pages/supervise.vue b/apps/rebreak-magic/app/pages/supervise.vue index e04b4dc..0706afd 100644 --- a/apps/rebreak-magic/app/pages/supervise.vue +++ b/apps/rebreak-magic/app/pages/supervise.vue @@ -54,7 +54,7 @@
- + Zurück