refactor: accept services prop in PatchTestsManagement and ServicesManagement

Add optional services prop so the page can pass pre-fetched data, avoiding duplicate API calls. Fall back to self-fetching when prop is not provided.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-07-05 22:26:08 +01:00
co-authored by Sisyphus
parent 42c3b65a27
commit 56eda0ead5
2 changed files with 57 additions and 17 deletions
@@ -24,6 +24,13 @@
created_by?: string;
};
interface Props {
services?: Service[];
onRefresh?: () => Promise<void>;
}
let { services: servicesProp, onRefresh }: Props = $props();
const durationOptions = Array.from({ length: 32 }, (_, i) => (i + 1) * 15);
let services = $state<Service[]>([]);
@@ -139,6 +146,12 @@
// =============== API Functions ===============
async function fetchServices() {
// If the parent provides services via prop, delegate refresh to parent
if (servicesProp && onRefresh) {
await onRefresh();
return;
}
servicesLoading = true;
try {
const response = await fetch('/api/admin/services', {
@@ -306,8 +319,18 @@
}
// =============== Lifecycle ===============
// Single effect: sync from prop when provided, self-fetch when not.
// Combined into one effect to prevent the reactive loop where
// fetchServices() → onRefresh() → parent updates → servicesProp changes → effect re-runs.
$effect(() => {
fetchServices();
if (servicesProp !== undefined) {
if (servicesProp.length > 0) {
services = servicesProp;
}
servicesLoading = false;
} else {
fetchServices();
}
});
</script>
@@ -570,7 +593,7 @@
id="service-duration"
bind:value={newService.duration_minutes}
onblur={validateDurationField}
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none {serviceErrors.duration_minutes
class="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-xs ring-offset-background transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 {serviceErrors.duration_minutes
? 'border-red-500'
: ''}"
>