typescript

This commit is contained in:
2025-11-13 02:37:16 +01:00
parent ada089c05b
commit 6d55e61cee
43 changed files with 923 additions and 406 deletions
+45 -16
View File
@@ -1,19 +1,45 @@
import { useCallback, useEffect, useState } from 'react';
const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }) => {
const [capabilitySets, setCapabilitySets] = useState([]);
type Identifier = string | number;
interface CapabilitySet {
id?: Identifier;
slug: string;
label?: string;
capabilities: string[];
[key: string]: unknown;
}
interface ApiClient {
get<T = CapabilitySet[]>(path: string): Promise<{ data: T }>;
post<T = CapabilitySet>(path: string, payload?: unknown): Promise<{ data: T }>;
patch<T = CapabilitySet>(path: string, payload?: unknown): Promise<{ data: T }>;
delete: (path: string) => Promise<void>;
}
interface UseCapabilitySetsOptions {
api: ApiClient;
notifyApiError?: (error: unknown, message: string) => void;
setStatusMessage?: (message: string, level?: string) => void;
token?: string | null;
}
const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }: UseCapabilitySetsOptions) => {
const [capabilitySets, setCapabilitySets] = useState<CapabilitySet[]>([]);
const [capabilitySetsLoading, setCapabilitySetsLoading] = useState(false);
const [creatingCapabilitySet, setCreatingCapabilitySet] = useState(false);
const [savingCapabilitySetId, setSavingCapabilitySetId] = useState(null);
const [deletingCapabilitySetId, setDeletingCapabilitySetId] = useState(null);
const [savingCapabilitySetId, setSavingCapabilitySetId] = useState<Identifier | null>(null);
const [deletingCapabilitySetId, setDeletingCapabilitySetId] = useState<Identifier | null>(null);
const [supportsCapabilitySetLabels, setSupportsCapabilitySetLabels] = useState(false);
const applyCapabilitySets = useCallback((updater) => {
const applyCapabilitySets = useCallback((updater: CapabilitySet[] | ((prev: CapabilitySet[]) => CapabilitySet[])) => {
setCapabilitySets((previous) => {
const base = Array.isArray(previous) ? [...previous] : [];
const next = typeof updater === 'function'
? updater(base)
: (Array.isArray(updater) ? [...updater] : base);
: Array.isArray(updater)
? [...updater]
: base;
const supportsLabels = next.some((item) => Object.prototype.hasOwnProperty.call(item || {}, 'label'));
setSupportsCapabilitySetLabels(supportsLabels);
return next;
@@ -27,7 +53,7 @@ const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }) =>
}
setCapabilitySetsLoading(true);
try {
const { data } = await api.get('/capability-sets');
const { data } = await api.get<CapabilitySet[]>('/capability-sets');
applyCapabilitySets(Array.isArray(data) ? data : []);
} catch (error) {
notifyApiError?.(error, 'Failed to load capability sets.');
@@ -45,7 +71,7 @@ const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }) =>
}, [applyCapabilitySets, refreshCapabilitySets, token]);
const createCapabilitySet = useCallback(
async ({ slug, label, capabilities } = {}) => {
async ({ slug, label, capabilities }: { slug?: string; label?: string; capabilities?: string[] } = {}) => {
if (creatingCapabilitySet) {
return false;
}
@@ -55,7 +81,7 @@ const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }) =>
}
setCreatingCapabilitySet(true);
try {
const payload = {
const payload: { slug?: string; label?: string; capabilities: string[] } = {
capabilities,
};
const trimmedSlug = slug?.trim();
@@ -67,12 +93,12 @@ const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }) =>
payload.label = trimmedLabel;
}
const { data } = await api.post('/capability-sets', payload);
const { data } = await api.post<CapabilitySet>('/capability-sets', payload);
if (data) {
applyCapabilitySets((previous) => {
const next = previous.filter((entry) => entry?.id !== data.id);
next.push(data);
next.sort((a, b) => a.slug.localeCompare(b.slug));
next.sort((a, b) => (a.slug || '').localeCompare(b.slug || ''));
return next;
});
} else {
@@ -99,13 +125,16 @@ const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }) =>
);
const updateCapabilitySet = useCallback(
async (capabilitySetId, { slug, label, capabilities } = {}) => {
async (
capabilitySetId: Identifier | null,
{ slug, label, capabilities }: { slug?: string; label?: string; capabilities?: string[] } = {},
) => {
if (!capabilitySetId) {
return false;
}
setSavingCapabilitySetId(capabilitySetId);
try {
const payload = {};
const payload: { slug?: string; label?: string; capabilities?: string[] } = {};
if (slug !== undefined) {
const trimmed = slug?.trim();
if (trimmed) {
@@ -126,7 +155,7 @@ const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }) =>
payload.capabilities = capabilities;
}
const { data } = await api.patch(`/capability-sets/${capabilitySetId}`, payload);
const { data } = await api.patch<CapabilitySet>(`/capability-sets/${capabilitySetId}`, payload);
if (data) {
applyCapabilitySets((previous) => {
let found = false;
@@ -140,7 +169,7 @@ const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }) =>
if (!found) {
next.push(data);
}
next.sort((a, b) => a.slug.localeCompare(b.slug));
next.sort((a, b) => (a.slug || '').localeCompare(b.slug || ''));
return next;
});
} else {
@@ -166,7 +195,7 @@ const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }) =>
);
const deleteCapabilitySet = useCallback(
async (capabilitySetId) => {
async (capabilitySetId: Identifier | null) => {
if (!capabilitySetId) {
return false;
}
+4 -4
View File
@@ -58,13 +58,13 @@ interface PasskeyRegisterPayload {
nickname?: string;
}
type RegisterPasskeyFailureReason = 'unsupported' | 'busy' | 'cancelled' | 'error';
type RegisterPasskeyResult =
export type RegisterPasskeyFailureReason = 'unsupported' | 'busy' | 'cancelled' | 'error';
export type RegisterPasskeyResult =
| { ok: true }
| { ok: false; reason: RegisterPasskeyFailureReason; message?: string };
type RevokePasskeyFailureReason = 'missing-id' | 'error';
type RevokePasskeyResult =
export type RevokePasskeyFailureReason = 'missing-id' | 'error';
export type RevokePasskeyResult =
| { ok: true }
| { ok: false; reason: RevokePasskeyFailureReason; message?: string };