Manage a resource's services from the panel
A "سرویسها" action on each resource row opens a modal listing what that resource performs, with its own duration and price. It follows the skills modal exactly — same PUT-replaces-everything contract, same components, no new page and no new route. Leaving a cell empty means inherit, so the effective value is shown as the placeholder along with where it came from: "40 — service default", "1,800,000 toman — branch". Without that the user cannot tell an unset field from a zero, which is the one thing this screen has to communicate. Two backend adjustments came out of wiring it up: - the offering filter in findEligible is now scoped to the requirement's resource type. Registering lasers for a service was making the room in the same plan ineligible and breaking the whole booking — "who performs this" is about the performing role, not about rooms and support resources. The seeder caught this immediately. - the scenario seeder now creates offerings and resource categories, so the demo data exercises this model instead of leaving every resource empty. Three vitest tests: inherited value with its source, saving an override, and clearing back to inheritance. Verified in the browser at 1440 dark, 1440 compact and 390 mobile — the last with no horizontal scroll. Panel suite 648 green across 98 files, tsc clean, encore build succeeds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { toast } from 'sonner';
|
||||
import { api, ApiError, type ApiResponse } from '../lib/api';
|
||||
import type {
|
||||
ClinicResource, ResourcePool, ResourcePayload, ResourceType, Skill,
|
||||
ClinicResource, ResourcePool, ResourcePayload, ResourceServiceOffering, ResourceType, Skill,
|
||||
} from '../types';
|
||||
|
||||
/**
|
||||
@@ -77,6 +77,36 @@ export function useResources(filters: ResourceFilters = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* سرویسهایی که یک منبع ارائه میدهد.
|
||||
*
|
||||
* مقدار مؤثر و منبعِ هر عدد از سرور میآید، نه از محاسبهٔ فرانت: زنجیرهٔ حل چهار سطح
|
||||
* دارد و بازسازیاش اینجا یعنی دو پیادهسازی که با هم واگرا میشوند.
|
||||
*/
|
||||
export function useResourceServices(resourceUuid?: string) {
|
||||
const qc = useQueryClient();
|
||||
const key = ['resource-services', resourceUuid];
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: key,
|
||||
queryFn: () => api.get<ApiResponse<ResourceServiceOffering[]>>(`/api/v1/resource/${resourceUuid}/services`),
|
||||
enabled: !!resourceUuid,
|
||||
});
|
||||
|
||||
const save = useMutation({
|
||||
mutationFn: ({ uuid, services }: { uuid: string; services: Array<Record<string, unknown>> }) =>
|
||||
api.put<ApiResponse<ResourceServiceOffering[]>>(`/api/v1/resource/${uuid}/services`, { services }),
|
||||
onSuccess: () => {
|
||||
toast.success('سرویسهای منبع ذخیره شد');
|
||||
qc.invalidateQueries({ queryKey: ['resource-services'] });
|
||||
qc.invalidateQueries({ queryKey: [RESOURCES_KEY] });
|
||||
},
|
||||
onError: (e) => fail(e, 'ذخیرهٔ سرویسها ناموفق بود'),
|
||||
});
|
||||
|
||||
return { offerings: query.data?.data ?? [], loading: query.isLoading, save };
|
||||
}
|
||||
|
||||
export function useResourceTypes() {
|
||||
const qc = useQueryClient();
|
||||
const invalidate = () => qc.invalidateQueries({ queryKey: TYPES_KEY });
|
||||
|
||||
Reference in New Issue
Block a user