diff --git a/app/(app)/clients/[id].tsx b/app/(app)/clients/[id].tsx
index 4c38346..ed649ac 100644
--- a/app/(app)/clients/[id].tsx
+++ b/app/(app)/clients/[id].tsx
@@ -1,5 +1,5 @@
import { useState } from 'react';
-import { View, Text, ScrollView, TouchableOpacity, ActivityIndicator, Linking, Alert, TextInput, Modal } from 'react-native';
+import { View, Text, ScrollView, TouchableOpacity, ActivityIndicator, Linking, Alert, TextInput, Modal, KeyboardAvoidingView, Platform } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useLocalSearchParams, router } from 'expo-router';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
@@ -67,6 +67,7 @@ function PayInvoiceModal({ invoice, onClose, onSuccess }: { invoice: any; onClos
return (
+
Record Payment
@@ -115,6 +116,7 @@ function PayInvoiceModal({ invoice, onClose, onSuccess }: { invoice: any; onClos
+
);
}
diff --git a/app/(app)/clients/index.tsx b/app/(app)/clients/index.tsx
index 641124a..71dfb31 100644
--- a/app/(app)/clients/index.tsx
+++ b/app/(app)/clients/index.tsx
@@ -10,8 +10,16 @@ const STATUS_CONFIG: Record}
contentContainerStyle={{ padding: 16, paddingBottom: 32 }}
renderItem={({ item }) => {
- const st = STATUS_CONFIG[item.status] ?? { label: item.status, color: '#475569', bg: '#F1F5F9', accent: '#94A3B8' };
+ const statusKey = getClientStatus(item);
+ const st = STATUS_CONFIG[statusKey] ?? { label: statusKey, color: '#475569', bg: '#F1F5F9', accent: '#94A3B8' };
const plan = item.subscriptions?.[0]?.plan?.name;
return (
+
Record Payment
@@ -82,6 +83,7 @@ function PayInvoiceModal({ invoice, onClose, onSuccess }: { invoice: any; onClos
+
);
}
diff --git a/app/(app)/tasks/[id].tsx b/app/(app)/tasks/[id].tsx
index f65e25a..b64e4b9 100644
--- a/app/(app)/tasks/[id].tsx
+++ b/app/(app)/tasks/[id].tsx
@@ -125,12 +125,22 @@ export default function TicketDetailScreen() {
: `Installation confirmed. Location recorded: ${coordStr}`;
await api.post(`/api/v1/tickets/${id}/messages`, { body: note }).catch(() => {});
+ // 4. Create follow-up activation ticket
+ await api.post('/api/v1/tickets', {
+ clientId: ticket?.clientId,
+ subject: `Account Activation — ${ticket?.client?.firstName ?? ''} ${ticket?.client?.lastName ?? ''}`.trim(),
+ type: 'BILLING',
+ priority: 'NORMAL',
+ description: `Follow-up after installation confirmed. Please activate the client account and generate the first invoice.\n\nInstallation ref: ${id}\nLocation: ${coordStr}`,
+ }).catch(() => {});
+
setInstNotes('');
setCoords(null);
- Alert.alert('Installation Complete!', 'Ticket resolved and client location updated.');
+ Alert.alert('Installation Complete! ✓', 'Ticket resolved, location updated, and activation ticket created.');
refetch();
qc.invalidateQueries({ queryKey: ['tasks'] });
qc.invalidateQueries({ queryKey: ['client', ticket?.clientId] });
+ qc.invalidateQueries({ queryKey: ['client-tickets', ticket?.clientId] });
setActiveTab('comments');
} catch {
Alert.alert('Error', 'Could not confirm installation. Please try again.');
@@ -404,7 +414,7 @@ export default function TicketDetailScreen() {
) : (
messages.map((m: any, i: number) => {
- const isSystem = m.senderType === 'SYSTEM' || m.message?.startsWith('Status changed') || m.message?.startsWith('Installation confirmed');
+ const isSystem = m.senderType === 'SYSTEM' || m.body?.startsWith('Status changed') || m.body?.startsWith('Installation confirmed');
const isMe = m.sender?.id === user?.id;
if (isSystem) {
@@ -412,7 +422,7 @@ export default function TicketDetailScreen() {
return (
- {m.message}
+ {m.body}
{m.createdAt && (
@@ -441,7 +451,7 @@ export default function TicketDetailScreen() {
borderWidth: isMe ? 0 : 1, borderColor: '#F1F5F9',
}}>
- {m.message}
+ {m.body}
{m.createdAt && (
diff --git a/app/(app)/tasks/index.tsx b/app/(app)/tasks/index.tsx
index 7c2f478..7dd37d4 100644
--- a/app/(app)/tasks/index.tsx
+++ b/app/(app)/tasks/index.tsx
@@ -2,6 +2,8 @@ import { useState } from 'react';
import { View, Text, FlatList, TextInput, TouchableOpacity, ActivityIndicator, RefreshControl, ScrollView } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useQuery } from '@tanstack/react-query';
+import { useFocusEffect } from 'expo-router';
+import { useCallback } from 'react';
import { router } from 'expo-router';
import { api } from '../../../services/api';
@@ -23,6 +25,9 @@ export default function TasksScreen() {
queryFn: () => api.get('/api/v1/tickets?limit=100').then(r => r.data?.data ?? r.data ?? []),
});
+ // Refetch list every time this screen comes into focus (fixes stale list after navigating back)
+ useFocusEffect(useCallback(() => { refetch(); }, []));
+
const tasks = (data ?? []).filter((t: any) => {
const matchSearch = `${t.subject} ${t.client?.firstName ?? ''} ${t.client?.lastName ?? ''}`.toLowerCase().includes(search.toLowerCase());
const matchStatus = statusFilter === 'All' || t.status === statusFilter;