Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/api/calls/calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const callsApi = createCachedApiEndpoint('/Calls/GetActiveCalls', {
const getCallApi = createApiEndpoint('/Calls/GetCall');
const getCallExtraDataApi = createApiEndpoint('/Calls/GetCallExtraData');
const createCallApi = createApiEndpoint('/Calls/SaveCall');
const updateCallApi = createApiEndpoint('/Calls/UpdateCall');
const updateCallApi = createApiEndpoint('/Calls/EditCall');
const closeCallApi = createApiEndpoint('/Calls/CloseCall');

export const getCalls = async () => {
Expand Down Expand Up @@ -96,16 +96,16 @@ const buildDispatchList = (data: { dispatchEveryone?: boolean; dispatchUsers?: s
const dispatchEntries: string[] = [];

if (data.dispatchUsers) {
dispatchEntries.push(...data.dispatchUsers);
dispatchEntries.push(...data.dispatchUsers.map((user) => `P:${user}`));
}
if (data.dispatchGroups) {
dispatchEntries.push(...data.dispatchGroups);
dispatchEntries.push(...data.dispatchGroups.map((group) => `G:${group}`));
}
if (data.dispatchRoles) {
dispatchEntries.push(...data.dispatchRoles);
dispatchEntries.push(...data.dispatchRoles.map((role) => `R:${role}`));
}
if (data.dispatchUnits) {
dispatchEntries.push(...data.dispatchUnits);
dispatchEntries.push(...data.dispatchUnits.map((unit) => `U:${unit}`));
}

return dispatchEntries.join('|');
Expand Down Expand Up @@ -147,7 +147,7 @@ export const updateCall = async (callData: UpdateCallRequest) => {
const dispatchList = buildDispatchList(callData);

const data = {
CallId: callData.callId,
Id: callData.callId,
Name: callData.name,
Nature: callData.nature,
Note: callData.note || '',
Expand All @@ -163,7 +163,7 @@ export const updateCall = async (callData: UpdateCallRequest) => {
DispatchList: dispatchList,
};

const response = await updateCallApi.post<SaveCallResult>(data);
const response = await updateCallApi.put<SaveCallResult>(data);

// Invalidate cache after successful mutation
try {
Expand Down
44 changes: 34 additions & 10 deletions src/app/call/[id]/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export default function EditCall() {
const callDataError = useCallsStore((state) => state.error);
const fetchCallFormData = useCallsStore((state) => state.fetchCallFormData);
const call = useCallDetailStore((state) => state.call);
const callExtraData = useCallDetailStore((state) => state.callExtraData);
const callDetailLoading = useCallDetailStore((state) => state.isLoading);
const callDetailError = useCallDetailStore((state) => state.error);
const fetchCallDetail = useCallDetailStore((state) => state.fetchCallDetail);
Expand Down Expand Up @@ -158,7 +159,35 @@ export default function EditCall() {
useEffect(() => {
if (call) {
const priority = callPriorities.find((p) => p.Id === call.Priority);
const type = callTypes.find((t) => t.Id === call.Type);
// Call.Type is the type's text, not its id -- matching on Id left the picker blank on every edit.
const type = callTypes.find((t) => t.Name === call.Type);

// Seed the picker with who the call already went to. Without this the edit posted an empty
// dispatch list, which the API reads as "dispatch the whole department".
const initialDispatch: DispatchSelection = {
everyone: false,
users: [],
groups: [],
roles: [],
units: [],
};

if (callExtraData?.Dispatches) {
callExtraData.Dispatches.forEach((dispatch) => {
const dispatchType = (dispatch.Type || '').toLowerCase();
if (dispatchType === 'personnel' || dispatchType === 'p' || dispatchType === 'user') {
initialDispatch.users.push(dispatch.Id);
} else if (dispatchType === 'group' || dispatchType === 'groups' || dispatchType === 'g') {
initialDispatch.groups.push(dispatch.Id);
} else if (dispatchType === 'role' || dispatchType === 'roles' || dispatchType === 'r') {
initialDispatch.roles.push(dispatch.Id);
} else if (dispatchType === 'unit' || dispatchType === 'units' || dispatchType === 'u') {
initialDispatch.units.push(dispatch.Id);
}
});
}

setDispatchSelection(initialDispatch);

reset({
name: call.Name || '',
Expand All @@ -175,13 +204,7 @@ export default function EditCall() {
type: type?.Name || '',
contactName: call.ContactName || '',
contactInfo: call.ContactInfo || '',
dispatchSelection: {
everyone: false,
users: [],
groups: [],
roles: [],
units: [],
},
dispatchSelection: initialDispatch,
});

// Set selected location if coordinates exist
Expand All @@ -193,7 +216,7 @@ export default function EditCall() {
});
}
}
}, [call, callPriorities, callTypes, reset]);
}, [call, callExtraData, callPriorities, callTypes, reset]);

// Track when edit call view is rendered
useEffect(() => {
Expand Down Expand Up @@ -226,7 +249,8 @@ export default function EditCall() {
name: data.name,
nature: data.nature,
priority: priority?.Id || 0,
type: type?.Id || '',
// The API matches the call type by its text, not its id.
type: type?.Name || '',
note: data.note,
address: data.address,
latitude: data.latitude,
Expand Down
3 changes: 2 additions & 1 deletion src/app/call/new/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ export default function NewCall() {
name: data.name,
nature: data.nature,
priority: priority.Id,
type: type.Id,
// The API matches the call type by its text, not its id.
type: type.Name,
note: data.note,
address: data.address,
latitude: data.latitude,
Expand Down
15 changes: 11 additions & 4 deletions src/stores/dispatch/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,23 @@ export const useDispatchStore = create<DispatchState>((set, get) => ({
const categorizedRoles: RecipientsResultData[] = [];
const categorizedUnits: RecipientsResultData[] = [];

// The recipients endpoint hands back wire ids ("P:<userId>", "R:12"). The selection is keyed on
// bare ids so it lines up with the ids a call's existing dispatches come back as, and the
// prefixes are put back on in the calls API when the dispatch list is built.
const stripPrefix = (id: string) => (id ? id.replace(/^[PGRU]:/, '') : id);

// Categorize recipients based on Type field
recipients.Data.forEach((recipient) => {
const entry = { ...recipient, Id: stripPrefix(recipient.Id) };

if (recipient.Type === 'Personnel') {
categorizedUsers.push(recipient);
categorizedUsers.push(entry);
} else if (recipient.Type === 'Groups') {
categorizedGroups.push(recipient);
categorizedGroups.push(entry);
} else if (recipient.Type === 'Roles') {
categorizedRoles.push(recipient);
categorizedRoles.push(entry);
} else if (recipient.Type === 'Unit') {
categorizedUnits.push(recipient);
categorizedUnits.push(entry);
}
});

Expand Down
Loading