chahinebrini 8851f36f65 fix(native): protectedDevices store — unwrap response shape
Backend GET /api/devices/protected returns:
  { success, data: { devices, plan, max, isLegend } }

apiFetch already unwraps `data`, leaving us with the object
{ devices, plan, max, isLegend } — not an array.

Old code did `Array.isArray(res) ? res : []` on that object, which
silently fell through to an empty list. Effect: enrolled protected
devices (Mac/Windows) never appeared in the Geräte screen even though
the DB row existed and the API responded correctly.

Fix: read res.devices instead of assuming the response is the array.
2026-05-15 23:02:26 +02:00

79 lines
2.2 KiB
TypeScript

import { create } from 'zustand';
import { apiFetch } from '../lib/api';
export type ProtectedDeviceStatus = 'pending' | 'active' | 'revoked' | 'degraded';
export interface ProtectedDevice {
id: string;
platform: 'mac' | 'windows' | string;
label: string;
status: ProtectedDeviceStatus;
installedAt: string | null;
createdAt: string;
}
export interface EnrollResult {
deviceId: string;
downloadUrl: string;
}
type ProtectedDevicesState = {
devices: ProtectedDevice[];
loading: boolean;
enrolling: boolean;
load: () => Promise<void>;
enroll: (label: string, platform: 'mac' | 'windows') => Promise<EnrollResult>;
confirmInstalled: (id: string) => Promise<void>;
remove: (id: string) => Promise<{ manualRemovalRequired: boolean }>;
};
export const useProtectedDevicesStore = create<ProtectedDevicesState>((set, get) => ({
devices: [],
loading: false,
enrolling: false,
load: async () => {
set({ loading: true });
try {
const res = await apiFetch<{ devices?: ProtectedDevice[] }>('/api/devices/protected');
set({ devices: Array.isArray(res?.devices) ? res.devices : [] });
} catch {
// endpoint might not be ready yet — keep empty state, screen handles it
} finally {
set({ loading: false });
}
},
enroll: async (label: string, platform: 'mac' | 'windows') => {
set({ enrolling: true });
try {
const result = await apiFetch<EnrollResult>('/api/devices/enroll', {
method: 'POST',
body: { platform, label },
});
await get().load();
return result;
} finally {
set({ enrolling: false });
}
},
confirmInstalled: async (id: string) => {
await apiFetch(`/api/devices/${id}/confirm-installed`, { method: 'POST' });
set((s) => ({
devices: s.devices.map((d) =>
d.id === id ? { ...d, status: 'active' as const, installedAt: new Date().toISOString() } : d
),
}));
},
remove: async (id: string) => {
const res = await apiFetch<{ manualRemovalRequired: boolean }>(`/api/devices/${id}/revoke`, {
method: 'DELETE',
});
set((s) => ({ devices: s.devices.filter((d) => d.id !== id) }));
return res;
},
}));