diff --git a/src/components/incident-command/__tests__/incident-command-tab-panel.test.tsx b/src/components/incident-command/__tests__/incident-command-tab-panel.test.tsx
index 111d9f3b..fe333337 100644
--- a/src/components/incident-command/__tests__/incident-command-tab-panel.test.tsx
+++ b/src/components/incident-command/__tests__/incident-command-tab-panel.test.tsx
@@ -26,6 +26,23 @@ jest.mock('lucide-react-native', () => ({
const { View } = require('react-native');
return ;
},
+ // Icons used by the incident chat section rendered inside the panel.
+ MessageCircle: () => {
+ const { View } = require('react-native');
+ return ;
+ },
+ MessagesSquare: () => {
+ const { View } = require('react-native');
+ return ;
+ },
+ ShieldCheck: () => {
+ const { View } = require('react-native');
+ return ;
+ },
+ Users: () => {
+ const { View } = require('react-native');
+ return ;
+ },
}));
jest.mock('@/lib/logging', () => ({
diff --git a/src/components/incident-command/incident-chat-section.tsx b/src/components/incident-command/incident-chat-section.tsx
new file mode 100644
index 00000000..ee4e3ff5
--- /dev/null
+++ b/src/components/incident-command/incident-chat-section.tsx
@@ -0,0 +1,139 @@
+import { router } from 'expo-router';
+import { MessageCircle, MessagesSquare, Radio, ShieldCheck, Users } from 'lucide-react-native';
+import React, { useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import { Box } from '@/components/ui/box';
+import { Heading } from '@/components/ui/heading';
+import { HStack } from '@/components/ui/hstack';
+import { Pressable } from '@/components/ui/pressable';
+import { Text } from '@/components/ui/text';
+import { VStack } from '@/components/ui/vstack';
+import { useDirectMessage } from '@/hooks/use-direct-message';
+import { type IncidentContactInfo, type ResourceIncidentView } from '@/models/v4/incidentCommand/resourceIncidentView';
+
+import { getIncidentRoleName } from './incident-role-names';
+
+interface IncidentChatSectionProps {
+ view: ResourceIncidentView;
+ testID?: string;
+}
+
+/** One tappable channel row. Rendered only when the server handed us an id we are allowed to open. */
+const ChannelRow: React.FC<{ label: string; hint?: string | null; icon: React.ElementType; channelId?: string | null; testID: string }> = ({ label, hint, icon: IconComponent, channelId, testID }) => {
+ const onPress = useCallback(() => {
+ if (channelId) {
+ router.push(`/chat/${channelId}`);
+ }
+ }, [channelId]);
+
+ if (!channelId) {
+ return null;
+ }
+
+ return (
+
+
+
+
+ {label}
+ {hint ? {hint} : null}
+
+
+
+ );
+};
+
+/** A person on the incident with a button to open a 1:1 with them. */
+const ContactRow: React.FC<{ label: string; contact?: IncidentContactInfo | null; onMessage: (userId?: string | null) => void; testID: string }> = ({ label, contact, onMessage, testID }) => {
+ const { t } = useTranslation();
+ const handlePress = useCallback(() => onMessage(contact?.UserId), [onMessage, contact]);
+
+ if (!contact) {
+ return null;
+ }
+
+ return (
+
+
+ {label}
+ {contact.Name}
+
+ {/* External contacts have a name and phone but no Resgrid account, so no 1:1 to open. */}
+ {contact.UserId ? (
+
+
+
+ ) : null}
+
+ );
+};
+
+/**
+ * Incident chat on the Command tab: the channels this responder can open, and the people on the
+ * incident they can reach 1:1.
+ *
+ * Access is decided entirely by the server — a channel id only appears in the payload when the
+ * caller is actually allowed in, and it disappears again when they are (for example) taken off the
+ * lane. Nothing here infers access on its own.
+ */
+export const IncidentChatSection: React.FC = ({ view, testID = 'incident-command-chat' }) => {
+ const { t } = useTranslation();
+ const { openDirectMessage } = useDirectMessage();
+
+ const chat = view.Chat;
+ const assignment = view.MyAssignment;
+ const roles = view.Roles ?? [];
+
+ const handleMessage = useCallback((userId?: string | null) => void openDirectMessage(userId), [openDirectMessage]);
+
+ const hasChannels = !!(chat?.IncidentChannelId || chat?.LaneChannelId || chat?.CommandChannelId || chat?.LeadsChannelId || chat?.DispatchChannelId);
+ // Only ICS role holders live here — the commander and this resource's lane leads already have
+ // contact cards higher up the panel, and listing them twice just made the screen noisy.
+ const hasContacts = roles.length > 0;
+
+ if (!hasChannels && !hasContacts) {
+ return null;
+ }
+
+ return (
+
+
+ {t('incident_command.chat')}
+
+
+ {chat?.IsFrozen ? (
+
+ {t('incident_command.chat_frozen')}
+
+ ) : null}
+
+ {hasChannels ? (
+
+
+
+
+
+
+
+ ) : null}
+
+ {hasContacts ? (
+
+ {t('incident_command.ics_positions')}
+ {roles.map((role) => (
+
+ ))}
+
+ ) : null}
+
+ );
+};
+
+export default IncidentChatSection;
diff --git a/src/components/incident-command/incident-command-tab-panel.tsx b/src/components/incident-command/incident-command-tab-panel.tsx
index c1556110..cc48ed54 100644
--- a/src/components/incident-command/incident-command-tab-panel.tsx
+++ b/src/components/incident-command/incident-command-tab-panel.tsx
@@ -1,5 +1,5 @@
import { format } from 'date-fns';
-import { MailIcon, PhoneIcon } from 'lucide-react-native';
+import { MailIcon, MessageCircle, PhoneIcon } from 'lucide-react-native';
import { useColorScheme } from 'nativewind';
import React, { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
@@ -13,10 +13,13 @@ import { Pressable } from '@/components/ui/pressable';
import { Spinner } from '@/components/ui/spinner';
import { Text } from '@/components/ui/text';
import { VStack } from '@/components/ui/vstack';
+import { useDirectMessage } from '@/hooks/use-direct-message';
import { logger } from '@/lib/logging';
import { type IncidentContactInfo, IncidentNeedStatus, type TacticalObjective, TacticalObjectiveStatus } from '@/models/v4/incidentCommand/resourceIncidentView';
import { useIncidentCommandStore } from '@/stores/calls/incident-command-store';
+import { IncidentChatSection } from './incident-chat-section';
+
interface IncidentCommandTabPanelProps {
callId: string;
}
@@ -102,6 +105,9 @@ interface ContactRowProps {
}
const ContactRow: React.FC = ({ label, contact, testID }) => {
+ const { t } = useTranslation();
+ const { openDirectMessage } = useDirectMessage();
+
return (
{label}
@@ -122,6 +128,15 @@ const ContactRow: React.FC = ({ label, contact, testID }) => {
) : null}
+ {/* External contacts carry a name and phone but no Resgrid account, so there is nobody to message. */}
+ {contact.UserId ? (
+ void openDirectMessage(contact.UserId)} testID={`${testID}-message`}>
+
+
+ {t('incident_command.send_message')}
+
+
+ ) : null}
);
};
@@ -264,6 +279,9 @@ export const IncidentCommandTabPanel: React.FC = (
+ {/* Incident chat: the channels this responder may open, plus who they can reach 1:1. */}
+
+
{/* Objectives */}
{t('incident_command.objectives')}
diff --git a/src/components/incident-command/incident-role-names.ts b/src/components/incident-command/incident-role-names.ts
new file mode 100644
index 00000000..d3d547d8
--- /dev/null
+++ b/src/components/incident-command/incident-role-names.ts
@@ -0,0 +1,45 @@
+import { type TFunction } from 'i18next';
+
+/**
+ * Standard NIMS/ICS position titles, keyed by the Core `IncidentRoleType` value.
+ *
+ * English titles rather than translation keys: these are the position names an IC is trained
+ * against, and the IC app treats them the same way. Translating them needs a subject-matter
+ * translator per locale, not a literal one.
+ */
+const ICS_ROLE_NAMES: Record = {
+ 0: 'Incident Commander',
+ 1: 'Deputy Incident Commander',
+ 2: 'Unified Command Member',
+ 3: 'Operations Section Chief',
+ 4: 'Planning Section Chief',
+ 5: 'Logistics Section Chief',
+ 6: 'Finance/Admin Section Chief',
+ 7: 'Safety Officer',
+ 8: 'Liaison Officer',
+ 9: 'Public Information Officer',
+ 10: 'Staging Area Manager',
+ 11: 'Resources Unit Leader',
+ 12: 'Situation Unit Leader',
+ 13: 'Documentation Unit Leader',
+ 14: 'Communications Unit Leader',
+ 15: 'Division/Group Supervisor',
+ 16: 'Branch Director',
+ 17: 'Strike Team/Task Force Leader',
+ 18: 'Medical Unit Leader',
+ 19: 'Rehab Officer',
+ 20: 'Medical Branch Director',
+ 21: 'Triage Officer',
+ 22: 'Treatment Officer',
+ 23: 'Transport Officer',
+ 24: 'HazMat Group Supervisor',
+ 25: 'Decon Officer',
+ 26: 'Entry Team Leader',
+ 27: 'Search Group Supervisor',
+ 28: 'Air Operations Branch Director',
+ 29: 'Shelter/Mass Care Coordinator',
+ 30: 'Damage Assessment Lead',
+};
+
+/** Display name for an ICS position; falls back to a generic label for a value we don't know yet. */
+export const getIncidentRoleName = (t: TFunction, roleType: number): string => ICS_ROLE_NAMES[roleType] ?? t('incident_command.role_generic', { role: roleType });
diff --git a/src/hooks/use-direct-message.ts b/src/hooks/use-direct-message.ts
new file mode 100644
index 00000000..1fe2a46d
--- /dev/null
+++ b/src/hooks/use-direct-message.ts
@@ -0,0 +1,48 @@
+import { router } from 'expo-router';
+import { useCallback, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import { createDirectMessage } from '@/api/chat/chat';
+import { logger } from '@/lib/logging';
+import { useToastStore } from '@/stores/toast/store';
+
+/**
+ * Opens a 1:1 conversation with someone and navigates to it.
+ *
+ * The server dedups on a normalized participant key, so calling this repeatedly for the same person
+ * reuses the existing conversation rather than starting a new one — which is what makes it safe to
+ * hang a "message" button off every contact on an incident.
+ */
+export const useDirectMessage = () => {
+ const { t } = useTranslation();
+ const [isOpening, setIsOpening] = useState(false);
+
+ const openDirectMessage = useCallback(
+ async (targetUserId?: string | null) => {
+ if (!targetUserId) {
+ // External lane leads have a name and phone but no Resgrid account to message.
+ useToastStore.getState().showToast('info', t('incident_command.dm_unavailable'));
+ return;
+ }
+
+ setIsOpening(true);
+ try {
+ const channel = await createDirectMessage({ TargetUserId: targetUserId });
+ const channelId = channel?.Data?.ChatChannelId;
+ if (!channelId) {
+ useToastStore.getState().showToast('error', t('incident_command.dm_failed'));
+ return;
+ }
+ router.push(`/chat/${channelId}`);
+ } catch (error) {
+ logger.error({ message: 'chat: failed to open direct message', context: { error, targetUserId } });
+ useToastStore.getState().showToast('error', t('incident_command.dm_failed'));
+ } finally {
+ setIsOpening(false);
+ }
+ },
+ [t]
+ );
+
+ return { openDirectMessage, isOpening };
+};
diff --git a/src/models/v4/chat/chatEnums.ts b/src/models/v4/chat/chatEnums.ts
index 006fd1c9..11916f0c 100644
--- a/src/models/v4/chat/chatEnums.ts
+++ b/src/models/v4/chat/chatEnums.ts
@@ -13,6 +13,10 @@ export enum ChatChannelType {
IncidentLane = 6,
IncidentCommand = 7,
Chatbot = 8,
+ /** IC plus every lane's primary/secondary lead — command talking to the people running the lanes. */
+ IncidentLeads = 9,
+ /** The incident's line to the dispatch desk: everyone on the incident, plus every authorized dispatcher. */
+ IncidentDispatch = 10,
}
/** Message type (ChatMessageResultData.MessageType). */
diff --git a/src/models/v4/incidentCommand/resourceIncidentView.ts b/src/models/v4/incidentCommand/resourceIncidentView.ts
index 5ea46fe4..c650472a 100644
--- a/src/models/v4/incidentCommand/resourceIncidentView.ts
+++ b/src/models/v4/incidentCommand/resourceIncidentView.ts
@@ -121,6 +121,33 @@ export class ResourceLaneAssignmentView {
public LinkedNeed?: IncidentNeed | null = null;
}
+/** Who holds an ICS position on the incident, with the contact details to reach them. */
+export class IncidentRoleContactInfo {
+ /** Maps to IncidentRoleType. */
+ public RoleType: number = 0;
+ public Contact?: IncidentContactInfo | null = null;
+}
+
+/**
+ * The incident's chat channels, already filtered by the server to the ones this caller may open.
+ * A null id means "not available to you" — not command staff, not a lane lead, or not provisioned.
+ * Never infer access from anything else: if the id isn't here, the channel will reject you.
+ */
+export class IncidentChatChannels {
+ /** Call-wide incident channel (everyone on the call). */
+ public IncidentChannelId?: string | null = null;
+ /** Private command channel — command staff (IC or an ICS role holder) only. */
+ public CommandChannelId?: string | null = null;
+ /** "All Leads" channel — the IC and lane primary/secondary leads only. */
+ public LeadsChannelId?: string | null = null;
+ /** The caller's own lane channel, when they are assigned to a lane. */
+ public LaneChannelId?: string | null = null;
+ /** The incident's line to the dispatch desk — open to everyone on the incident. */
+ public DispatchChannelId?: string | null = null;
+ /** True once the incident is closed: readable, but frozen as a point-in-time record. */
+ public IsFrozen: boolean = false;
+}
+
export class ResourceIncidentView {
public IncidentCommandId: string = '';
public CallId: number = 0;
@@ -136,4 +163,8 @@ export class ResourceIncidentView {
public Notes: IncidentNote[] = [];
public Attachments: IncidentAttachment[] = [];
public MyAssignment?: ResourceLaneAssignmentView | null = null;
+ /** ICS positions filled on this incident, so a crew can reach the right person directly. */
+ public Roles?: IncidentRoleContactInfo[] = [];
+ /** Chat channels this caller may open. */
+ public Chat?: IncidentChatChannels | null = null;
}
diff --git a/src/translations/ar.json b/src/translations/ar.json
index ee4901f4..f7ed5dd6 100644
--- a/src/translations/ar.json
+++ b/src/translations/ar.json
@@ -565,13 +565,27 @@
"action_plan": "خطة عمل الحادث",
"assigned_since": "تم التعيين {{time}}",
"attachments": "المرفقات",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff only",
"commander": "القائد",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "Reach the dispatch desk",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
"error": "فشل في تحميل معلومات قيادة الحادث",
"established": "تم التأسيس",
"estimated_end": "النهاية المتوقعة",
+ "ics_positions": "ICS positions",
"important_information": "معلومات مهمة",
+ "incident_channel": "Incident chat",
"incident_info": "معلومات الحادث",
+ "lane_channel": "Lane chat",
+ "leads_channel": "All Leads",
+ "leads_channel_hint": "IC and lane leads",
"linked_need": "احتياج مرتبط",
+ "message_person": "Message {{name}}",
"my_assignment": "تعيين الوحدة",
"need_category": {
"equipment": "معدات",
@@ -609,8 +623,11 @@
"primary_objective": "الهدف الرئيسي",
"progress": "اكتمل {{percent}}%",
"quantity_fulfilled": "تم تلبية {{fulfilled}} من {{requested}}",
+ "reach_directly": "Reach directly",
+ "role_generic": "ICS role {{role}}",
"secondary_lead": "المسؤول الثانوي",
"secondary_objective": "الهدف الثانوي",
+ "send_message": "Message",
"tab_title": "القيادة"
},
"livekit": {
diff --git a/src/translations/de.json b/src/translations/de.json
index c2b0f0e8..6679c7a1 100644
--- a/src/translations/de.json
+++ b/src/translations/de.json
@@ -565,13 +565,27 @@
"action_plan": "Einsatzplan",
"assigned_since": "Zugewiesen {{time}}",
"attachments": "Anhänge",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff only",
"commander": "Einsatzleiter",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "Reach the dispatch desk",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
"error": "Informationen zur Einsatzleitung konnten nicht geladen werden",
"established": "Eingerichtet",
"estimated_end": "Voraussichtliches Ende",
+ "ics_positions": "ICS positions",
"important_information": "Wichtige Informationen",
+ "incident_channel": "Incident chat",
"incident_info": "Vorfallinformationen",
+ "lane_channel": "Lane chat",
+ "leads_channel": "All Leads",
+ "leads_channel_hint": "IC and lane leads",
"linked_need": "Verknüpfter Bedarf",
+ "message_person": "Message {{name}}",
"my_assignment": "Einheitszuweisung",
"need_category": {
"equipment": "Ausrüstung",
@@ -609,8 +623,11 @@
"primary_objective": "Primäres Ziel",
"progress": "{{percent}} % abgeschlossen",
"quantity_fulfilled": "{{fulfilled}} von {{requested}} erfüllt",
+ "reach_directly": "Reach directly",
+ "role_generic": "ICS role {{role}}",
"secondary_lead": "Sekundäre Leitung",
"secondary_objective": "Sekundäres Ziel",
+ "send_message": "Message",
"tab_title": "Führung"
},
"livekit": {
diff --git a/src/translations/en.json b/src/translations/en.json
index ae9c5dd3..81bddae1 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -565,13 +565,27 @@
"action_plan": "Incident Action Plan",
"assigned_since": "Assigned {{time}}",
"attachments": "Attachments",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff only",
"commander": "Commander",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "Reach the dispatch desk",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
"error": "Failed to load incident command information",
"established": "Established",
"estimated_end": "Estimated End",
+ "ics_positions": "ICS positions",
"important_information": "Important Information",
+ "incident_channel": "Incident chat",
"incident_info": "Incident Information",
+ "lane_channel": "Lane chat",
+ "leads_channel": "All Leads",
+ "leads_channel_hint": "IC and lane leads",
"linked_need": "Linked Need",
+ "message_person": "Message {{name}}",
"my_assignment": "Unit Assignment",
"need_category": {
"equipment": "Equipment",
@@ -609,8 +623,11 @@
"primary_objective": "Primary Objective",
"progress": "{{percent}}% complete",
"quantity_fulfilled": "{{fulfilled}} of {{requested}} fulfilled",
+ "reach_directly": "Reach directly",
+ "role_generic": "ICS role {{role}}",
"secondary_lead": "Secondary Lead",
"secondary_objective": "Secondary Objective",
+ "send_message": "Message",
"tab_title": "Command"
},
"livekit": {
diff --git a/src/translations/es.json b/src/translations/es.json
index 1463f127..db3eb4fb 100644
--- a/src/translations/es.json
+++ b/src/translations/es.json
@@ -565,13 +565,27 @@
"action_plan": "Plan de acción del incidente",
"assigned_since": "Asignado {{time}}",
"attachments": "Adjuntos",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff only",
"commander": "Comandante",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "Reach the dispatch desk",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
"error": "Error al cargar la información del comando del incidente",
"established": "Establecido",
"estimated_end": "Fin estimado",
+ "ics_positions": "ICS positions",
"important_information": "Información importante",
+ "incident_channel": "Incident chat",
"incident_info": "Información del incidente",
+ "lane_channel": "Lane chat",
+ "leads_channel": "All Leads",
+ "leads_channel_hint": "IC and lane leads",
"linked_need": "Necesidad vinculada",
+ "message_person": "Message {{name}}",
"my_assignment": "Asignación de la unidad",
"need_category": {
"equipment": "Equipamiento",
@@ -609,8 +623,11 @@
"primary_objective": "Objetivo principal",
"progress": "{{percent}}% completado",
"quantity_fulfilled": "{{fulfilled}} de {{requested}} completados",
+ "reach_directly": "Reach directly",
+ "role_generic": "ICS role {{role}}",
"secondary_lead": "Líder secundario",
"secondary_objective": "Objetivo secundario",
+ "send_message": "Message",
"tab_title": "Comando"
},
"livekit": {
diff --git a/src/translations/fr.json b/src/translations/fr.json
index 55d36ef3..3ffda709 100644
--- a/src/translations/fr.json
+++ b/src/translations/fr.json
@@ -565,13 +565,27 @@
"action_plan": "Plan d'action de l'incident",
"assigned_since": "Affecté {{time}}",
"attachments": "Pièces jointes",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff only",
"commander": "Commandant",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "Reach the dispatch desk",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
"error": "Échec du chargement des informations du commandement de l'incident",
"established": "Établi",
"estimated_end": "Fin estimée",
+ "ics_positions": "ICS positions",
"important_information": "Informations importantes",
+ "incident_channel": "Incident chat",
"incident_info": "Informations sur l'incident",
+ "lane_channel": "Lane chat",
+ "leads_channel": "All Leads",
+ "leads_channel_hint": "IC and lane leads",
"linked_need": "Besoin lié",
+ "message_person": "Message {{name}}",
"my_assignment": "Affectation de l'unité",
"need_category": {
"equipment": "Équipement",
@@ -609,8 +623,11 @@
"primary_objective": "Objectif principal",
"progress": "{{percent}} % terminé",
"quantity_fulfilled": "{{fulfilled}} sur {{requested}} satisfaits",
+ "reach_directly": "Reach directly",
+ "role_generic": "ICS role {{role}}",
"secondary_lead": "Responsable secondaire",
"secondary_objective": "Objectif secondaire",
+ "send_message": "Message",
"tab_title": "Commandement"
},
"livekit": {
diff --git a/src/translations/it.json b/src/translations/it.json
index 8f94ceae..498c2ed8 100644
--- a/src/translations/it.json
+++ b/src/translations/it.json
@@ -565,13 +565,27 @@
"action_plan": "Piano d'azione dell'incidente",
"assigned_since": "Assegnata {{time}}",
"attachments": "Allegati",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff only",
"commander": "Comandante",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "Reach the dispatch desk",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
"error": "Impossibile caricare le informazioni del comando incidente",
"established": "Istituito",
"estimated_end": "Fine stimata",
+ "ics_positions": "ICS positions",
"important_information": "Informazioni importanti",
+ "incident_channel": "Incident chat",
"incident_info": "Informazioni sull'incidente",
+ "lane_channel": "Lane chat",
+ "leads_channel": "All Leads",
+ "leads_channel_hint": "IC and lane leads",
"linked_need": "Necessità collegata",
+ "message_person": "Message {{name}}",
"my_assignment": "Assegnazione unità",
"need_category": {
"equipment": "Attrezzatura",
@@ -609,8 +623,11 @@
"primary_objective": "Obiettivo principale",
"progress": "{{percent}}% completato",
"quantity_fulfilled": "{{fulfilled}} di {{requested}} soddisfatti",
+ "reach_directly": "Reach directly",
+ "role_generic": "ICS role {{role}}",
"secondary_lead": "Responsabile secondario",
"secondary_objective": "Obiettivo secondario",
+ "send_message": "Message",
"tab_title": "Comando"
},
"livekit": {
diff --git a/src/translations/pl.json b/src/translations/pl.json
index 015b79bd..300ebdad 100644
--- a/src/translations/pl.json
+++ b/src/translations/pl.json
@@ -565,13 +565,27 @@
"action_plan": "Plan działań incydentu",
"assigned_since": "Przydzielono {{time}}",
"attachments": "Załączniki",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff only",
"commander": "Dowódca",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "Reach the dispatch desk",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
"error": "Nie udało się załadować informacji o dowodzeniu incydentem",
"established": "Ustanowiono",
"estimated_end": "Szacowane zakończenie",
+ "ics_positions": "ICS positions",
"important_information": "Ważne informacje",
+ "incident_channel": "Incident chat",
"incident_info": "Informacje o incydencie",
+ "lane_channel": "Lane chat",
+ "leads_channel": "All Leads",
+ "leads_channel_hint": "IC and lane leads",
"linked_need": "Powiązana potrzeba",
+ "message_person": "Message {{name}}",
"my_assignment": "Przydział jednostki",
"need_category": {
"equipment": "Sprzęt",
@@ -609,8 +623,11 @@
"primary_objective": "Cel główny",
"progress": "Ukończono {{percent}}%",
"quantity_fulfilled": "Zaspokojono {{fulfilled}} z {{requested}}",
+ "reach_directly": "Reach directly",
+ "role_generic": "ICS role {{role}}",
"secondary_lead": "Pomocniczy kierujący",
"secondary_objective": "Cel drugorzędny",
+ "send_message": "Message",
"tab_title": "Dowodzenie"
},
"livekit": {
diff --git a/src/translations/sv.json b/src/translations/sv.json
index 726d2f37..2925e4f2 100644
--- a/src/translations/sv.json
+++ b/src/translations/sv.json
@@ -565,13 +565,27 @@
"action_plan": "Insatsplan",
"assigned_since": "Tilldelad {{time}}",
"attachments": "Bilagor",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff only",
"commander": "Insatsledare",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "Reach the dispatch desk",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
"error": "Det gick inte att ladda information om insatsledningen",
"established": "Upprättad",
"estimated_end": "Beräknat slut",
+ "ics_positions": "ICS positions",
"important_information": "Viktig information",
+ "incident_channel": "Incident chat",
"incident_info": "Incidentinformation",
+ "lane_channel": "Lane chat",
+ "leads_channel": "All Leads",
+ "leads_channel_hint": "IC and lane leads",
"linked_need": "Kopplat behov",
+ "message_person": "Message {{name}}",
"my_assignment": "Enhetstilldelning",
"need_category": {
"equipment": "Utrustning",
@@ -609,8 +623,11 @@
"primary_objective": "Primärt mål",
"progress": "{{percent}} % slutfört",
"quantity_fulfilled": "{{fulfilled}} av {{requested}} uppfyllda",
+ "reach_directly": "Reach directly",
+ "role_generic": "ICS role {{role}}",
"secondary_lead": "Biträdande ansvarig",
"secondary_objective": "Sekundärt mål",
+ "send_message": "Message",
"tab_title": "Ledning"
},
"livekit": {
diff --git a/src/translations/uk.json b/src/translations/uk.json
index f8548ca5..76a46afb 100644
--- a/src/translations/uk.json
+++ b/src/translations/uk.json
@@ -565,13 +565,27 @@
"action_plan": "План дій щодо інциденту",
"assigned_since": "Призначено {{time}}",
"attachments": "Вкладення",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff only",
"commander": "Керівник",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "Reach the dispatch desk",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
"error": "Не вдалося завантажити інформацію про керування інцидентом",
"established": "Встановлено",
"estimated_end": "Орієнтовне завершення",
+ "ics_positions": "ICS positions",
"important_information": "Важлива інформація",
+ "incident_channel": "Incident chat",
"incident_info": "Інформація про інцидент",
+ "lane_channel": "Lane chat",
+ "leads_channel": "All Leads",
+ "leads_channel_hint": "IC and lane leads",
"linked_need": "Пов'язана потреба",
+ "message_person": "Message {{name}}",
"my_assignment": "Призначення підрозділу",
"need_category": {
"equipment": "Обладнання",
@@ -609,8 +623,11 @@
"primary_objective": "Основна ціль",
"progress": "Виконано {{percent}}%",
"quantity_fulfilled": "Виконано {{fulfilled}} з {{requested}}",
+ "reach_directly": "Reach directly",
+ "role_generic": "ICS role {{role}}",
"secondary_lead": "Додатковий відповідальний",
"secondary_objective": "Другорядна ціль",
+ "send_message": "Message",
"tab_title": "Керування"
},
"livekit": {