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:
@@ -23,6 +23,12 @@
|
|||||||
service_ids: string[];
|
service_ids: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
services?: Service[];
|
||||||
|
}
|
||||||
|
|
||||||
|
let { services: servicesProp }: Props = $props();
|
||||||
|
|
||||||
let patchTests = $state<PatchTest[]>([]);
|
let patchTests = $state<PatchTest[]>([]);
|
||||||
let availableServices = $state<Service[]>([]);
|
let availableServices = $state<Service[]>([]);
|
||||||
let loading = $state(true);
|
let loading = $state(true);
|
||||||
@@ -99,25 +105,29 @@
|
|||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
loading = true;
|
loading = true;
|
||||||
try {
|
try {
|
||||||
const [ptRes, svcRes] = await Promise.all([
|
const ptRes = await fetch('/api/admin/patch-tests', {
|
||||||
fetch('/api/admin/patch-tests', {
|
|
||||||
headers: { Authorization: `Bearer ${authStore.currentToken}` }
|
headers: { Authorization: `Bearer ${authStore.currentToken}` }
|
||||||
}),
|
});
|
||||||
fetch('/api/admin/services', {
|
|
||||||
headers: { Authorization: `Bearer ${authStore.currentToken}` }
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (ptRes.ok && svcRes.ok) {
|
if (ptRes.ok) {
|
||||||
patchTests = await ptRes.json();
|
patchTests = await ptRes.json();
|
||||||
|
} else {
|
||||||
|
toast.error('Failed to load patch test data');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only fetch services if not provided via prop
|
||||||
|
if (!servicesProp) {
|
||||||
|
const svcRes = await fetch('/api/admin/services', {
|
||||||
|
headers: { Authorization: `Bearer ${authStore.currentToken}` }
|
||||||
|
});
|
||||||
|
if (svcRes.ok) {
|
||||||
const svcData = await svcRes.json();
|
const svcData = await svcRes.json();
|
||||||
const uniqueSvc = new SvelteMap<string, Service>();
|
const uniqueSvc = new SvelteMap<string, Service>();
|
||||||
svcData.forEach((s: Service) => {
|
svcData.forEach((s: Service) => {
|
||||||
if (s.id) uniqueSvc.set(s.id, s);
|
if (s.id) uniqueSvc.set(s.id, s);
|
||||||
});
|
});
|
||||||
availableServices = Array.from(uniqueSvc.values());
|
availableServices = Array.from(uniqueSvc.values());
|
||||||
} else {
|
}
|
||||||
toast.error('Failed to load patch test data');
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -241,6 +251,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reactively update availableServices when the parent provides services via prop
|
||||||
|
$effect(() => {
|
||||||
|
if (servicesProp !== undefined) {
|
||||||
|
availableServices = servicesProp;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,6 +24,13 @@
|
|||||||
created_by?: string;
|
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);
|
const durationOptions = Array.from({ length: 32 }, (_, i) => (i + 1) * 15);
|
||||||
|
|
||||||
let services = $state<Service[]>([]);
|
let services = $state<Service[]>([]);
|
||||||
@@ -139,6 +146,12 @@
|
|||||||
|
|
||||||
// =============== API Functions ===============
|
// =============== API Functions ===============
|
||||||
async function fetchServices() {
|
async function fetchServices() {
|
||||||
|
// If the parent provides services via prop, delegate refresh to parent
|
||||||
|
if (servicesProp && onRefresh) {
|
||||||
|
await onRefresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
servicesLoading = true;
|
servicesLoading = true;
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/admin/services', {
|
const response = await fetch('/api/admin/services', {
|
||||||
@@ -306,8 +319,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// =============== Lifecycle ===============
|
// =============== 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(() => {
|
$effect(() => {
|
||||||
|
if (servicesProp !== undefined) {
|
||||||
|
if (servicesProp.length > 0) {
|
||||||
|
services = servicesProp;
|
||||||
|
}
|
||||||
|
servicesLoading = false;
|
||||||
|
} else {
|
||||||
fetchServices();
|
fetchServices();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -570,7 +593,7 @@
|
|||||||
id="service-duration"
|
id="service-duration"
|
||||||
bind:value={newService.duration_minutes}
|
bind:value={newService.duration_minutes}
|
||||||
onblur={validateDurationField}
|
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'
|
? 'border-red-500'
|
||||||
: ''}"
|
: ''}"
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user