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
62 changes: 62 additions & 0 deletions guard_app/src/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -286,5 +286,67 @@
"testNotif": "测试通知",
"notifTitle": "已分配班次",
"notifBody": "您已被分配到医院综合楼班次。"
},
"payroll": {
"period": "期间:",
"guardID": "保安ID:",
"site": "地点:",
"department": "部门:",
"notFound": "未找到工资单",
"error": "加载工资单记录失败",
"types": {
"daily": "每日",
"weekly": "每周",
"monthly": "每月"
},
"filter": "筛选",
"startDateHead": "开始日期*",
"selectStart": "选择开始日期",
"endDateHead": "结束日期*",
"selectEnd": "选择结束日期",
"periodHead": "期间类型*",
"selectPeriod": "选择期间",
"optional": "可选",
"ID": "ID",
"siteHead": "地点",
"siteHint": "地点名称",
"departmentHead": "部门",
"departmentHint": "部门名称",
"missingAlertHead": "信息缺失",
"missingAlertMsg": "请选择开始日期、结束日期以及期间类型。",
"invalidAlertHead": "无效信息",
"invalidAlertMsg": "开始日期必须早于结束日期。",
"cancel": "取消"
},
"incidentReport": {
"title": "事件报告",
"newReport": "新建报告",
"reportId": "报告ID:",
"date": "日期:",
"time": "时间:",
"location": "地点:",
"description": "描述:",
"reportedBy": "报告人:",
"severity": "严重程度:",
"status": "状态:",
"types": {
"low": "低",
"medium": "中",
"high": "高",
"critical": "严重"
},
"statusTypes": {
"open": "开启",
"inProgress": "处理中",
"resolved": "已解决",
"closed": "已关闭"
},
"noReports": "未找到事件报告",
"error": "加载事件报告失败",
"submit": "提交报告",
"submitting": "提交中...",
"submitSuccess": "事件报告提交成功",
"submitFailed": "提交事件报告失败",
"cancel": "取消"
}
}
62 changes: 62 additions & 0 deletions guard_app/src/locales/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -286,5 +286,67 @@
"testNotif": "測試通知",
"notifTitle": "已分配班次",
"notifBody": "您已被分配到醫院綜合大樓班次。"
},
"payroll": {
"period": "期間:",
"guardID": "保安ID:",
"site": "地點:",
"department": "部門:",
"notFound": "未找到薪資單",
"error": "載入薪資單紀錄失敗",
"types": {
"daily": "每日",
"weekly": "每週",
"monthly": "每月"
},
"filter": "篩選",
"startDateHead": "開始日期*",
"selectStart": "選擇開始日期",
"endDateHead": "結束日期*",
"selectEnd": "選擇結束日期",
"periodHead": "期間類型*",
"selectPeriod": "選擇期間",
"optional": "可選",
"ID": "ID",
"siteHead": "地點",
"siteHint": "地點名稱",
"departmentHead": "部門",
"departmentHint": "部門名稱",
"missingAlertHead": "資訊缺失",
"missingAlertMsg": "請選擇開始日期、結束日期以及期間類型。",
"invalidAlertHead": "無效資訊",
"invalidAlertMsg": "開始日期必須早於結束日期。",
"cancel": "取消"
},
"incidentReport": {
"title": "事件報告",
"newReport": "新增報告",
"reportId": "報告ID:",
"date": "日期:",
"time": "時間:",
"location": "地點:",
"description": "描述:",
"reportedBy": "報告人:",
"severity": "嚴重程度:",
"status": "狀態:",
"types": {
"low": "低",
"medium": "中",
"high": "高",
"critical": "嚴重"
},
"statusTypes": {
"open": "開啟",
"inProgress": "處理中",
"resolved": "已解決",
"closed": "已關閉"
},
"noReports": "未找到事件報告",
"error": "載入事件報告失敗",
"submit": "提交報告",
"submitting": "提交中...",
"submitSuccess": "事件報告提交成功",
"submitFailed": "提交事件報告失敗",
"cancel": "取消"
}
}
2 changes: 1 addition & 1 deletion guard_app/src/navigation/AppNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export default function AppNavigator() {
<Stack.Screen
name="IncidentReports"
component={IncidentReportScreen}
options={{ headerShown: true, title: 'Incident Reports' }}
options={{ headerShown: true, title: t('incidentReport.title') }}
/>
<Stack.Screen
name="QRScanner"
Expand Down
40 changes: 23 additions & 17 deletions guard_app/src/screen/IncidentReportScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useFocusEffect } from '@react-navigation/native';
import * as ImagePicker from 'expo-image-picker';
import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
ActivityIndicator,
Alert,
Expand Down Expand Up @@ -47,6 +48,7 @@ type ErrorState = {
} | null;

export default function IncidentReportScreen() {
const { t } = useTranslation();
const { colors } = useAppTheme();
const s = getStyles(colors);

Expand Down Expand Up @@ -77,7 +79,7 @@ export default function IncidentReportScreen() {
} catch (err: unknown) {
const message =
(err as { response?: { data?: { message?: string } } })?.response?.data?.message ??
(err instanceof Error ? err.message : 'Failed to load incidents. Please try again.');
(err instanceof Error ? err.message : t('incidentReport.error'));
setErrorState({ title: 'Failed to Load', message });
} finally {
setLoadingList(false);
Expand Down Expand Up @@ -169,8 +171,8 @@ export default function IncidentReportScreen() {

const successMessage =
failedUploads > 0
? `Incident report submitted successfully, but ${failedUploads} photo(s) failed to upload.`
: 'Incident report submitted successfully.';
? `${t('incidentReport.submitSuccess')}, but ${failedUploads} photo(s) failed to upload.`
: t('incidentReport.submitSuccess');

Alert.alert('Success', successMessage);
setSelectedShift(null);
Expand All @@ -181,9 +183,7 @@ export default function IncidentReportScreen() {
} catch (err: unknown) {
const message =
(err as { response?: { data?: { message?: string } } })?.response?.data?.message ??
(err instanceof Error
? err.message
: 'Could not submit the incident report. Please try again.');
(err instanceof Error ? err.message : t('incidentReport.submitFailed'));
setErrorState({ title: 'Submission Failed', message });
} finally {
setSubmitting(false);
Expand All @@ -194,11 +194,11 @@ export default function IncidentReportScreen() {
<>
<ScrollView contentContainerStyle={s.contentContainer} style={s.container}>
{/* Incident List */}
<Text style={s.title}>My Reports</Text>
<Text style={s.title}>{t('incidentReport.title')}</Text>
{loadingList ? (
<ActivityIndicator color={colors.primary} style={s.loader} />
) : incidents.length === 0 ? (
<Text style={s.emptyText}>No incidents reported yet.</Text>
<Text style={s.emptyText}>{t('incidentReport.noReports')}</Text>
) : (
incidents.map((item) => (
<View key={item._id} style={s.incidentCard}>
Expand All @@ -208,7 +208,11 @@ export default function IncidentReportScreen() {
</Text>
<Text style={s.incidentSeverity}>{item.severity}</Text>
</View>
{!!item.status && <Text style={s.incidentStatus}>Status: {item.status}</Text>}
{!!item.status && (
<Text style={s.incidentStatus}>
{t('incidentReport.status')} {item.status}
</Text>
)}
{!!item.createdAt && (
<Text style={s.incidentDate}>{new Date(item.createdAt).toLocaleString()}</Text>
)}
Expand All @@ -217,7 +221,7 @@ export default function IncidentReportScreen() {
)}

{/* Submit Form */}
<Text style={[s.title, s.formTitle]}>Incident Report</Text>
<Text style={[s.title, s.formTitle]}>{t('incidentReport.newReport')}</Text>

<Text style={s.label}>Shift *</Text>
<TouchableOpacity style={s.dropdown} onPress={() => setShowShiftPicker(true)}>
Expand All @@ -228,20 +232,22 @@ export default function IncidentReportScreen() {
</Text>
</TouchableOpacity>

<Text style={s.label}>Incident Description *</Text>
<Text style={s.label}>{t('incidentReport.description')}</Text>
<TextInput
value={description}
onChangeText={setDescription}
placeholder="Describe what happened..."
placeholder=""
placeholderTextColor={colors.muted}
multiline
style={s.textArea}
/>

<Text style={s.label}>Date &amp; Time *</Text>
<Text style={s.label}>
{t('incidentReport.date')} &amp; {t('incidentReport.time')}
</Text>
<Text style={s.readOnly}>{dateTime}</Text>

<Text style={s.label}>Severity *</Text>
<Text style={s.label}>{t('incidentReport.severity')}</Text>
<View style={s.row}>
{(['Low', 'Medium', 'High'] as Severity[]).map((lvl) => (
<TouchableOpacity
Expand All @@ -252,7 +258,7 @@ export default function IncidentReportScreen() {
<Text
style={[s.severityText, { color: severity === lvl ? colors.white : colors.text }]}
>
{lvl}
{t(`incidentReport.types.${lvl.toLowerCase()}`)}
</Text>
</TouchableOpacity>
))}
Expand All @@ -273,7 +279,7 @@ export default function IncidentReportScreen() {
{submitting ? (
<ActivityIndicator color={colors.white} />
) : (
<Text style={s.submitText}>Submit Report</Text>
<Text style={s.submitText}>{t('incidentReport.submit')}</Text>
)}
</TouchableOpacity>
</ScrollView>
Expand Down Expand Up @@ -303,7 +309,7 @@ export default function IncidentReportScreen() {
)}
</ScrollView>
<TouchableOpacity style={s.modalClose} onPress={() => setShowShiftPicker(false)}>
<Text style={s.modalCloseText}>Cancel</Text>
<Text style={s.modalCloseText}>{t('incidentReport.cancel')}</Text>
</TouchableOpacity>
</View>
</View>
Expand Down
Loading