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
115 changes: 115 additions & 0 deletions guard_app/src/components/ShiftDetailsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import React from 'react';
import { Modal, StyleSheet, Text, TouchableOpacity, View } from 'react-native';

export interface ShiftDetailsModalProps {
visible: boolean;
shift: unknown;
onClose: () => void;
}

export default function ShiftDetailsModal({ visible, shift, onClose }: ShiftDetailsModalProps) {
if (!shift || typeof shift !== 'object') return null;

const s = shift as any;

Check warning on line 13 in guard_app/src/components/ShiftDetailsModal.tsx

View workflow job for this annotation

GitHub Actions / ESLint & Prettier

Unexpected any. Specify a different type

Check warning on line 13 in guard_app/src/components/ShiftDetailsModal.tsx

View workflow job for this annotation

GitHub Actions / ESLint & Prettier

Unexpected any. Specify a different type

const statusColor =
s.status === 'Confirmed'

Check failure on line 16 in guard_app/src/components/ShiftDetailsModal.tsx

View workflow job for this annotation

GitHub Actions / ESLint & Prettier

Replace `⏎······?·'#22c55e'⏎······:·s.status·===·'Pending'⏎······?·'#3b82f6'⏎·····` with `·?·'#22c55e'·:·s.status·===·'Pending'·?·'#3b82f6'`

Check failure on line 16 in guard_app/src/components/ShiftDetailsModal.tsx

View workflow job for this annotation

GitHub Actions / ESLint & Prettier

Replace `⏎······?·'#22c55e'⏎······:·s.status·===·'Pending'⏎······?·'#3b82f6'⏎·····` with `·?·'#22c55e'·:·s.status·===·'Pending'·?·'#3b82f6'`
? '#22c55e'
: s.status === 'Pending'
? '#3b82f6'
: '#9ca3af';

return (
<Modal visible={visible} animationType="slide" transparent onRequestClose={onClose}>
<View style={styles.backdrop}>
<View style={styles.card}>
<View style={[styles.statusPill, { backgroundColor: statusColor }]} />

<Text style={styles.title}>{s.title ?? 'Shift Details'}</Text>

<Text style={styles.text}>
{s.date} · {s.time}
</Text>

{s.site && <Text style={styles.text}>{s.site}</Text>}
{s.rate && <Text style={styles.text}>{s.rate}</Text>}

<Text style={styles.status}>Status: {s.status}</Text>

<View style={styles.buttonsRow}>
{s.status === 'applied' && (
<TouchableOpacity style={styles.secondaryButton}>
<Text style={styles.secondaryButtonText}>Cancel Application</Text>
</TouchableOpacity>
)}

<TouchableOpacity style={styles.primaryButton} onPress={onClose}>
<Text style={styles.primaryButtonText}>Close</Text>
</TouchableOpacity>
</View>
</View>
</View>
</Modal>
);
}

const styles = StyleSheet.create({
backdrop: {
flex: 1,
backgroundColor: 'rgba(0,0,0,0.35)',
justifyContent: 'center',
padding: 16,
},
card: {
backgroundColor: 'white',
borderRadius: 12,
padding: 16,
},
statusPill: {
height: 6,
width: 48,
borderRadius: 4,
alignSelf: 'center',
marginBottom: 12,
},
title: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 8,
},
text: {
marginBottom: 4,
},
status: {
marginTop: 8,
fontWeight: '600',
},
buttonsRow: {
flexDirection: 'row',
marginTop: 16,
justifyContent: 'flex-end',
gap: 8,
},
primaryButton: {
paddingVertical: 10,
paddingHorizontal: 16,
borderRadius: 8,
backgroundColor: '#003f88',
},
primaryButtonText: {
color: '#fff',
fontWeight: '600',
},
secondaryButton: {
paddingVertical: 10,
paddingHorizontal: 16,
borderRadius: 8,
borderWidth: 1,
borderColor: '#ccc',
backgroundColor: '#f5f5f5',
},
secondaryButtonText: {
color: '#333',
fontWeight: '500',
},
});
207 changes: 207 additions & 0 deletions guard_app/src/screen/IncidentReportScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
import React, { useState } from 'react';

import {
View,
Text,
StyleSheet,
TextInput,
TouchableOpacity,
Alert,
ScrollView,
Image,
ActivityIndicator,
} from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import { COLORS } from '../theme/colors';

type Severity = 'Low' | 'Medium' | 'High';

/* --- date & time */
const getNowDateTime = () => new Date().toISOString().slice(0, 16).replace('T', ' ');

export default function IncidentReportScreen() {
const [description, setDescription] = useState('');
const [severity, setSeverity] = useState<Severity | null>(null);
const [images, setImages] = useState<string[]>([]);
const [submitting, setSubmitting] = useState(false);

/* --- auto date & time (CHANGED) --- */
const [dateTime] = useState(getNowDateTime());

/* Pick image */
const pickImage = async () => {
const res = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 0.7,
allowsMultipleSelection: true,
});

if (!res.canceled) {
setImages((prev) => [...prev, ...res.assets.map((a) => a.uri)]);
}
};

/* Submit (mocked) */
const submitReport = async () => {
if (!description.trim() || !severity) {
Alert.alert('Missing fields', 'Please fill all required fields.');
return;
}

setSubmitting(true);

// simulate API call
setTimeout(() => {
setSubmitting(false);
Alert.alert('Success', 'Incident report submitted successfully.');

// reset form
setDescription('');
setSeverity(null);
setImages([]);
}, 1200);
};

return (
<ScrollView style={s.screen} contentContainerStyle={{ paddingBottom: 32 }}>
<Text style={s.title}>Incident Report</Text>

{/* Description */}
<Text style={s.label}>Incident Description *</Text>
<TextInput
value={description}
onChangeText={setDescription}
placeholder="Describe what happened..."
multiline
style={s.textArea}
/>

{/* Date & Time (CHANGED) */}
<Text style={s.label}>Date & Time *</Text>
<Text style={s.readOnly}>{dateTime}</Text>

{/* Severity */}
<Text style={s.label}>Severity *</Text>
<View style={s.row}>
{(['Low', 'Medium', 'High'] as Severity[]).map((lvl) => (
<TouchableOpacity
key={lvl}
style={[s.severityBtn, severity === lvl && s.severitySelected]}
onPress={() => setSeverity(lvl)}
>
<Text
style={{
color: severity === lvl ? '#fff' : COLORS.text,
fontWeight: '600',
}}
>
{lvl}
</Text>
</TouchableOpacity>
))}
</View>

{/* Photos */}
<Text style={s.label}>Photos (optional)</Text>
<TouchableOpacity style={s.photoBtn} onPress={pickImage}>
<Text style={{ color: '#fff', fontWeight: '600' }}>Add Photos</Text>
</TouchableOpacity>

<ScrollView horizontal style={{ marginTop: 10 }}>
{images.map((uri) => (
<Image key={uri} source={{ uri }} style={s.preview} />
))}
</ScrollView>

{/* Submit */}
<TouchableOpacity style={s.submitBtn} onPress={submitReport} disabled={submitting}>
{submitting ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={s.submitText}>Submit Report</Text>
)}
</TouchableOpacity>
</ScrollView>
);
}

const s = StyleSheet.create({
screen: {
flex: 1,
backgroundColor: COLORS.bg,
padding: 16,
},
title: {
fontSize: 22,
fontWeight: '800',
marginBottom: 16,
color: COLORS.text,
},
label: {
fontSize: 14,
fontWeight: '600',
marginTop: 12,
marginBottom: 6,
color: COLORS.text,
},
textArea: {
height: 140,
backgroundColor: '#fff',
borderRadius: 12,
borderWidth: 1,
borderColor: '#E5E7EB',
padding: 12,
textAlignVertical: 'top',
},
input: {
backgroundColor: '#fff',
padding: 12,
borderRadius: 12,
borderWidth: 1,
borderColor: '#E5E7EB',
},
readOnly: {
backgroundColor: '#F3F4F6',
padding: 12,
borderRadius: 12,
color: '#6B7280',
},
row: {
flexDirection: 'row',
gap: 8,
},
severityBtn: {
flex: 1,
paddingVertical: 10,
borderRadius: 10,
backgroundColor: '#E5E7EB',
alignItems: 'center',
},
severitySelected: {
backgroundColor: COLORS.primary,
},
photoBtn: {
backgroundColor: COLORS.primary,
paddingVertical: 12,
borderRadius: 10,
alignItems: 'center',
},
preview: {
width: 70,
height: 70,
borderRadius: 8,
marginRight: 8,
},
submitBtn: {
marginTop: 24,
backgroundColor: COLORS.primary,
paddingVertical: 14,
borderRadius: 12,
alignItems: 'center',
},
submitText: {
color: '#fff',
fontWeight: '700',
fontSize: 16,
},
});
Loading
Loading