@ubkinfotech/tecaher-erp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +413 -0
  3. package/lib/commonjs/core/api/apiClient.js +38 -0
  4. package/lib/commonjs/core/api/endpoints.js +139 -0
  5. package/lib/commonjs/core/api/interceptor.js +64 -0
  6. package/lib/commonjs/core/auth/authContext.js +30 -0
  7. package/lib/commonjs/core/auth/authService.js +12 -0
  8. package/lib/commonjs/core/hooks/useApiQuery.js +26 -0
  9. package/lib/commonjs/core/provider/ERPProvider.js +63 -0
  10. package/lib/commonjs/core/provider/types.js +5 -0
  11. package/lib/commonjs/core/provider/useERP.js +17 -0
  12. package/lib/commonjs/core/types/api.js +1 -0
  13. package/lib/commonjs/index.js +110 -0
  14. package/lib/commonjs/modules/assignment/hooks/useAssignmentList.js +16 -0
  15. package/lib/commonjs/modules/assignment/hooks/useHomeworkDetails.js +16 -0
  16. package/lib/commonjs/modules/assignment/hooks/useHomeworkSubmissions.js +16 -0
  17. package/lib/commonjs/modules/assignment/screens/AssignmentScreen.js +2615 -0
  18. package/lib/commonjs/modules/assignment/services/assignmentService.js +75 -0
  19. package/lib/commonjs/modules/attendance/hooks/useAttendance.js +16 -0
  20. package/lib/commonjs/modules/attendance/screens/AttendanceScreen.js +866 -0
  21. package/lib/commonjs/modules/attendance/services/attendanceService.js +31 -0
  22. package/lib/commonjs/modules/leaveRequest/hooks/useLeaveRequests.js +16 -0
  23. package/lib/commonjs/modules/leaveRequest/screens/LeaveRequestScreen.js +991 -0
  24. package/lib/commonjs/modules/leaveRequest/services/leaveRequestService.js +42 -0
  25. package/lib/commonjs/modules/marks/hooks/useMarks.js +14 -0
  26. package/lib/commonjs/modules/marks/screens/MarksScreen.js +1621 -0
  27. package/lib/commonjs/modules/marks/services/marksService.js +71 -0
  28. package/lib/commonjs/modules/myAttendance/hooks/useMyAttendance.js +16 -0
  29. package/lib/commonjs/modules/myAttendance/screens/MyAttendanceScreen.js +357 -0
  30. package/lib/commonjs/modules/myAttendance/services/myAttendanceService.js +11 -0
  31. package/lib/commonjs/modules/notes/hooks/useNotes.js +16 -0
  32. package/lib/commonjs/modules/notes/screens/NotesScreen.js +1287 -0
  33. package/lib/commonjs/modules/notes/services/notesService.js +65 -0
  34. package/lib/commonjs/modules/noticeboard/hooks/useNoticeboard.js +16 -0
  35. package/lib/commonjs/modules/noticeboard/screens/NoticeBoardScreen.js +381 -0
  36. package/lib/commonjs/modules/noticeboard/services/noticeboardService.js +16 -0
  37. package/lib/commonjs/modules/notification/hooks/useNotifications.js +16 -0
  38. package/lib/commonjs/modules/notification/screens/NotificationScreen.js +186 -0
  39. package/lib/commonjs/modules/notification/services/notificationService.js +16 -0
  40. package/lib/commonjs/modules/promoteStudent/hooks/usePromoteStudent.js +16 -0
  41. package/lib/commonjs/modules/promoteStudent/screens/PromoteStudentScreen.js +644 -0
  42. package/lib/commonjs/modules/promoteStudent/services/promoteStudentService.js +36 -0
  43. package/lib/commonjs/modules/timetable/hooks/useTimeTable.js +14 -0
  44. package/lib/commonjs/modules/timetable/screens/TimeTableScreen.js +258 -0
  45. package/lib/commonjs/modules/timetable/services/timetableService.js +16 -0
  46. package/lib/commonjs/package.json +1 -0
  47. package/lib/commonjs/shared/empty-states/EmptyState.js +45 -0
  48. package/lib/commonjs/shared/empty-states/ErrorState.js +45 -0
  49. package/lib/commonjs/shared/loaders/LoadingState.js +25 -0
  50. package/lib/commonjs/shared/theme/theme.js +22 -0
  51. package/lib/module/core/api/apiClient.js +32 -0
  52. package/lib/module/core/api/endpoints.js +135 -0
  53. package/lib/module/core/api/interceptor.js +60 -0
  54. package/lib/module/core/auth/authContext.js +23 -0
  55. package/lib/module/core/auth/authService.js +8 -0
  56. package/lib/module/core/hooks/useApiQuery.js +21 -0
  57. package/lib/module/core/provider/ERPProvider.js +56 -0
  58. package/lib/module/core/provider/types.js +3 -0
  59. package/lib/module/core/provider/useERP.js +12 -0
  60. package/lib/module/core/types/api.js +1 -0
  61. package/lib/module/index.js +17 -0
  62. package/lib/module/modules/assignment/hooks/useAssignmentList.js +12 -0
  63. package/lib/module/modules/assignment/hooks/useHomeworkDetails.js +12 -0
  64. package/lib/module/modules/assignment/hooks/useHomeworkSubmissions.js +12 -0
  65. package/lib/module/modules/assignment/screens/AssignmentScreen.js +2609 -0
  66. package/lib/module/modules/assignment/services/assignmentService.js +62 -0
  67. package/lib/module/modules/attendance/hooks/useAttendance.js +12 -0
  68. package/lib/module/modules/attendance/screens/AttendanceScreen.js +860 -0
  69. package/lib/module/modules/attendance/services/attendanceService.js +23 -0
  70. package/lib/module/modules/leaveRequest/hooks/useLeaveRequests.js +12 -0
  71. package/lib/module/modules/leaveRequest/screens/LeaveRequestScreen.js +985 -0
  72. package/lib/module/modules/leaveRequest/services/leaveRequestService.js +35 -0
  73. package/lib/module/modules/marks/hooks/useMarks.js +10 -0
  74. package/lib/module/modules/marks/screens/MarksScreen.js +1615 -0
  75. package/lib/module/modules/marks/services/marksService.js +55 -0
  76. package/lib/module/modules/myAttendance/hooks/useMyAttendance.js +12 -0
  77. package/lib/module/modules/myAttendance/screens/MyAttendanceScreen.js +351 -0
  78. package/lib/module/modules/myAttendance/services/myAttendanceService.js +7 -0
  79. package/lib/module/modules/notes/hooks/useNotes.js +12 -0
  80. package/lib/module/modules/notes/screens/NotesScreen.js +1281 -0
  81. package/lib/module/modules/notes/services/notesService.js +54 -0
  82. package/lib/module/modules/noticeboard/hooks/useNoticeboard.js +12 -0
  83. package/lib/module/modules/noticeboard/screens/NoticeBoardScreen.js +375 -0
  84. package/lib/module/modules/noticeboard/services/noticeboardService.js +12 -0
  85. package/lib/module/modules/notification/hooks/useNotifications.js +12 -0
  86. package/lib/module/modules/notification/screens/NotificationScreen.js +180 -0
  87. package/lib/module/modules/notification/services/notificationService.js +12 -0
  88. package/lib/module/modules/promoteStudent/hooks/usePromoteStudent.js +12 -0
  89. package/lib/module/modules/promoteStudent/screens/PromoteStudentScreen.js +638 -0
  90. package/lib/module/modules/promoteStudent/services/promoteStudentService.js +27 -0
  91. package/lib/module/modules/timetable/hooks/useTimeTable.js +10 -0
  92. package/lib/module/modules/timetable/screens/TimeTableScreen.js +252 -0
  93. package/lib/module/modules/timetable/services/timetableService.js +11 -0
  94. package/lib/module/package.json +1 -0
  95. package/lib/module/shared/empty-states/EmptyState.js +40 -0
  96. package/lib/module/shared/empty-states/ErrorState.js +40 -0
  97. package/lib/module/shared/loaders/LoadingState.js +20 -0
  98. package/lib/module/shared/theme/theme.js +18 -0
  99. package/lib/typescript/core/api/apiClient.d.ts +14 -0
  100. package/lib/typescript/core/api/endpoints.d.ts +164 -0
  101. package/lib/typescript/core/api/interceptor.d.ts +4 -0
  102. package/lib/typescript/core/auth/authContext.d.ts +11 -0
  103. package/lib/typescript/core/auth/authService.d.ts +9 -0
  104. package/lib/typescript/core/hooks/useApiQuery.d.ts +7 -0
  105. package/lib/typescript/core/provider/ERPProvider.d.ts +14 -0
  106. package/lib/typescript/core/provider/types.d.ts +34 -0
  107. package/lib/typescript/core/provider/useERP.d.ts +3 -0
  108. package/lib/typescript/core/types/api.d.ts +11 -0
  109. package/lib/typescript/index.d.ts +18 -0
  110. package/lib/typescript/modules/assignment/hooks/useAssignmentList.d.ts +3 -0
  111. package/lib/typescript/modules/assignment/hooks/useHomeworkDetails.d.ts +4 -0
  112. package/lib/typescript/modules/assignment/hooks/useHomeworkSubmissions.d.ts +4 -0
  113. package/lib/typescript/modules/assignment/screens/AssignmentScreen.d.ts +9 -0
  114. package/lib/typescript/modules/assignment/services/assignmentService.d.ts +89 -0
  115. package/lib/typescript/modules/attendance/hooks/useAttendance.d.ts +3 -0
  116. package/lib/typescript/modules/attendance/screens/AttendanceScreen.d.ts +5 -0
  117. package/lib/typescript/modules/attendance/services/attendanceService.d.ts +33 -0
  118. package/lib/typescript/modules/leaveRequest/hooks/useLeaveRequests.d.ts +3 -0
  119. package/lib/typescript/modules/leaveRequest/screens/LeaveRequestScreen.d.ts +5 -0
  120. package/lib/typescript/modules/leaveRequest/services/leaveRequestService.d.ts +39 -0
  121. package/lib/typescript/modules/marks/hooks/useMarks.d.ts +2 -0
  122. package/lib/typescript/modules/marks/screens/MarksScreen.d.ts +6 -0
  123. package/lib/typescript/modules/marks/services/marksService.d.ts +150 -0
  124. package/lib/typescript/modules/myAttendance/hooks/useMyAttendance.d.ts +3 -0
  125. package/lib/typescript/modules/myAttendance/screens/MyAttendanceScreen.d.ts +5 -0
  126. package/lib/typescript/modules/myAttendance/services/myAttendanceService.d.ts +10 -0
  127. package/lib/typescript/modules/notes/hooks/useNotes.d.ts +3 -0
  128. package/lib/typescript/modules/notes/screens/NotesScreen.d.ts +8 -0
  129. package/lib/typescript/modules/notes/services/notesService.d.ts +58 -0
  130. package/lib/typescript/modules/noticeboard/hooks/useNoticeboard.d.ts +3 -0
  131. package/lib/typescript/modules/noticeboard/screens/NoticeBoardScreen.d.ts +5 -0
  132. package/lib/typescript/modules/noticeboard/services/noticeboardService.d.ts +17 -0
  133. package/lib/typescript/modules/notification/hooks/useNotifications.d.ts +3 -0
  134. package/lib/typescript/modules/notification/screens/NotificationScreen.d.ts +5 -0
  135. package/lib/typescript/modules/notification/services/notificationService.d.ts +8 -0
  136. package/lib/typescript/modules/promoteStudent/hooks/usePromoteStudent.d.ts +3 -0
  137. package/lib/typescript/modules/promoteStudent/screens/PromoteStudentScreen.d.ts +5 -0
  138. package/lib/typescript/modules/promoteStudent/services/promoteStudentService.d.ts +40 -0
  139. package/lib/typescript/modules/timetable/hooks/useTimeTable.d.ts +3 -0
  140. package/lib/typescript/modules/timetable/screens/TimeTableScreen.d.ts +5 -0
  141. package/lib/typescript/modules/timetable/services/timetableService.d.ts +14 -0
  142. package/lib/typescript/shared/empty-states/EmptyState.d.ts +6 -0
  143. package/lib/typescript/shared/empty-states/ErrorState.d.ts +6 -0
  144. package/lib/typescript/shared/loaders/LoadingState.d.ts +3 -0
  145. package/lib/typescript/shared/theme/theme.d.ts +17 -0
  146. package/package.json +89 -0
@@ -0,0 +1,991 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.LeaveRequestScreen = LeaveRequestScreen;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _useERP = require("../../../core/provider/useERP");
10
+ var _EmptyState = require("../../../shared/empty-states/EmptyState");
11
+ var _leaveRequestService = require("../services/leaveRequestService");
12
+ var _jsxRuntime = require("react/jsx-runtime");
13
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
14
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
15
+ function normalizeLeaveItems(result) {
16
+ if (Array.isArray(result)) {
17
+ return result;
18
+ }
19
+ if (Array.isArray(result?.data)) {
20
+ return result.data;
21
+ }
22
+ if (Array.isArray(result?.data?.data)) {
23
+ return result.data.data;
24
+ }
25
+ return [];
26
+ }
27
+ function normalizeLeaveTypes(result) {
28
+ const list = Array.isArray(result?.data) ? result.data : Array.isArray(result) ? result : [];
29
+ return list.map((item, index) => ({
30
+ label: String(item?.type ?? item?.label ?? `Type ${index + 1}`),
31
+ value: item?.id ?? item?.value ?? index + 1
32
+ }));
33
+ }
34
+ function tryGetCalendar() {
35
+ try {
36
+ const mod = require('react-native-calendars');
37
+ return mod?.Calendar ?? mod?.default ?? mod;
38
+ } catch {
39
+ return null;
40
+ }
41
+ }
42
+ function todayYmd() {
43
+ const d = new Date();
44
+ const y = d.getFullYear();
45
+ const m = String(d.getMonth() + 1).padStart(2, '0');
46
+ const day = String(d.getDate()).padStart(2, '0');
47
+ return `${y}-${m}-${day}`;
48
+ }
49
+ function addDays(d, days) {
50
+ const next = new Date(d);
51
+ next.setDate(next.getDate() + days);
52
+ return next;
53
+ }
54
+ function toYmd(d) {
55
+ const y = d.getFullYear();
56
+ const m = String(d.getMonth() + 1).padStart(2, '0');
57
+ const day = String(d.getDate()).padStart(2, '0');
58
+ return `${y}-${m}-${day}`;
59
+ }
60
+ function rangeMarkedDates(start, end) {
61
+ if (!start || !end) {
62
+ return {};
63
+ }
64
+ const s = new Date(start);
65
+ const e = new Date(end);
66
+ if (Number.isNaN(s.getTime()) || Number.isNaN(e.getTime())) {
67
+ return {};
68
+ }
69
+ const from = s <= e ? s : e;
70
+ const to = s <= e ? e : s;
71
+ const out = {};
72
+ let cur = new Date(from);
73
+ while (cur <= to) {
74
+ out[toYmd(cur)] = {
75
+ color: '#2563EB',
76
+ textColor: 'white'
77
+ };
78
+ cur = addDays(cur, 1);
79
+ }
80
+ out[toYmd(from)] = {
81
+ startingDay: true,
82
+ color: '#2563EB',
83
+ textColor: 'white'
84
+ };
85
+ out[toYmd(to)] = {
86
+ endingDay: true,
87
+ color: '#2563EB',
88
+ textColor: 'white'
89
+ };
90
+ return out;
91
+ }
92
+ function formatDate(dateString) {
93
+ if (!dateString) {
94
+ return '-';
95
+ }
96
+ const date = new Date(dateString);
97
+ if (Number.isNaN(date.getTime())) {
98
+ return dateString;
99
+ }
100
+ return date.toLocaleDateString('en-US', {
101
+ weekday: 'short',
102
+ day: '2-digit',
103
+ month: 'short'
104
+ });
105
+ }
106
+ function statusColors(status) {
107
+ const normalized = String(status ?? '').toLowerCase();
108
+ if (normalized === 'approved') {
109
+ return {
110
+ bg: '#E7F6E7',
111
+ text: '#2E7D32'
112
+ };
113
+ }
114
+ if (normalized === 'pending') {
115
+ return {
116
+ bg: '#FFF8E1',
117
+ text: '#F57C00'
118
+ };
119
+ }
120
+ if (normalized === 'declined') {
121
+ return {
122
+ bg: '#FFEBEE',
123
+ text: '#C62828'
124
+ };
125
+ }
126
+ return {
127
+ bg: '#F5F5F5',
128
+ text: '#757575'
129
+ };
130
+ }
131
+ function LeaveRequestCard({
132
+ item
133
+ }) {
134
+ const colors = statusColors(String(item?.status ?? ''));
135
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
136
+ style: styles.requestCard,
137
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
138
+ style: styles.requestTopRow,
139
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
140
+ style: {
141
+ flex: 1,
142
+ marginRight: 8
143
+ },
144
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
145
+ style: styles.requestTitle,
146
+ children: String(item?.employee_remark ?? 'Leave Request')
147
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
148
+ style: styles.requestType,
149
+ children: String(item?.type ?? '')
150
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
151
+ style: styles.requestDate,
152
+ children: [formatDate(String(item?.leave_from ?? '')), " -", ' ', formatDate(String(item?.leave_to ?? ''))]
153
+ })]
154
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
155
+ style: [styles.statusChip, {
156
+ backgroundColor: colors.bg
157
+ }],
158
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
159
+ style: [styles.statusChipText, {
160
+ color: colors.text
161
+ }],
162
+ children: String(item?.status ?? '').toLowerCase() || 'unknown'
163
+ })
164
+ })]
165
+ })
166
+ });
167
+ }
168
+ function LeaveRequestSkeleton() {
169
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
170
+ style: styles.skeletonWrap,
171
+ children: [1, 2, 3, 4, 5].map(item => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
172
+ style: styles.skeletonCard
173
+ }, item))
174
+ });
175
+ }
176
+ function SelectModal({
177
+ label,
178
+ value,
179
+ options,
180
+ placeholder,
181
+ onChange
182
+ }) {
183
+ const [open, setOpen] = (0, _react.useState)(false);
184
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
185
+ style: styles.field,
186
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
187
+ style: styles.label,
188
+ children: label
189
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
190
+ onPress: () => setOpen(true),
191
+ style: styles.select,
192
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
193
+ style: styles.selectText,
194
+ children: value?.label ?? placeholder
195
+ })
196
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
197
+ visible: open,
198
+ transparent: true,
199
+ animationType: "fade",
200
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
201
+ style: styles.modalOverlay,
202
+ onPress: () => setOpen(false),
203
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
204
+ style: styles.modalCard,
205
+ onPress: () => {},
206
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
207
+ style: styles.modalTitle,
208
+ children: label
209
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
210
+ data: options,
211
+ keyExtractor: (item, index) => `${item.value}-${index}`,
212
+ renderItem: ({
213
+ item
214
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
215
+ style: styles.optionRow,
216
+ onPress: () => {
217
+ onChange(item);
218
+ setOpen(false);
219
+ },
220
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
221
+ style: styles.optionText,
222
+ children: item.label
223
+ })
224
+ }),
225
+ ListEmptyComponent: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
226
+ style: styles.optionEmpty,
227
+ children: "No options"
228
+ })
229
+ })]
230
+ })
231
+ })
232
+ })]
233
+ });
234
+ }
235
+ function ApplyLeaveView({
236
+ leaveTypes,
237
+ onCancel,
238
+ onSuccess
239
+ }) {
240
+ const {
241
+ api
242
+ } = (0, _useERP.useERP)();
243
+ const Calendar = (0, _react.useMemo)(() => tryGetCalendar(), []);
244
+ const [reason, setReason] = (0, _react.useState)('');
245
+ const [leaveType, setLeaveType] = (0, _react.useState)(null);
246
+ const [startDate, setStartDate] = (0, _react.useState)('');
247
+ const [endDate, setEndDate] = (0, _react.useState)('');
248
+ const [dateMode, setDateMode] = (0, _react.useState)('start');
249
+ const [calendarOpen, setCalendarOpen] = (0, _react.useState)(false);
250
+ const [uploadOpen, setUploadOpen] = (0, _react.useState)(false);
251
+ const [busy, setBusy] = (0, _react.useState)(false);
252
+ const [markedDates, setMarkedDates] = (0, _react.useState)({});
253
+ const [fileName, setFileName] = (0, _react.useState)('');
254
+ const [filePath, setFilePath] = (0, _react.useState)(undefined);
255
+ (0, _react.useEffect)(() => {
256
+ setMarkedDates(rangeMarkedDates(startDate, endDate));
257
+ }, [startDate, endDate]);
258
+ const pickDocument = (0, _react.useCallback)(async () => {
259
+ let dp = null;
260
+ try {
261
+ dp = require('react-native-document-picker');
262
+ } catch {}
263
+ if (!dp?.pickSingle) {
264
+ _reactNative.Alert.alert('Error', 'Document picker is not available');
265
+ return;
266
+ }
267
+ const selected = await dp.pickSingle({
268
+ presentationStyle: 'fullScreen',
269
+ type: [dp.types.pdf]
270
+ });
271
+ const file = {
272
+ uri: selected.uri,
273
+ name: selected.name ?? 'document.pdf',
274
+ type: selected.type ?? 'application/pdf'
275
+ };
276
+ setFileName(file.name);
277
+ setBusy(true);
278
+ try {
279
+ const res = await (0, _leaveRequestService.uploadLeaveFile)(api, file);
280
+ if (res?.Status === 'Success' && res?.data) {
281
+ setFilePath(res.data);
282
+ } else {
283
+ setFileName('');
284
+ setFilePath(undefined);
285
+ _reactNative.Alert.alert('Error', 'File upload failed');
286
+ }
287
+ } catch (error) {
288
+ setFileName('');
289
+ setFilePath(undefined);
290
+ _reactNative.Alert.alert('Error', String(error?.message ?? 'File upload failed'));
291
+ } finally {
292
+ setBusy(false);
293
+ setUploadOpen(false);
294
+ }
295
+ }, [api]);
296
+ const pickCamera = (0, _react.useCallback)(async () => {
297
+ let picker = null;
298
+ try {
299
+ picker = require('react-native-image-picker');
300
+ } catch {}
301
+ if (!picker?.launchCamera) {
302
+ _reactNative.Alert.alert('Error', 'Camera picker is not available');
303
+ return;
304
+ }
305
+ const response = await new Promise(resolve => {
306
+ picker.launchCamera({
307
+ mediaType: 'photo',
308
+ saveToPhotos: true,
309
+ includeBase64: false
310
+ }, res => resolve(res));
311
+ });
312
+ const asset = response?.assets?.[0];
313
+ if (!asset?.uri) {
314
+ setUploadOpen(false);
315
+ return;
316
+ }
317
+ const file = {
318
+ uri: asset.uri,
319
+ name: asset.fileName ?? 'camera.jpg',
320
+ type: asset.type ?? 'image/jpeg'
321
+ };
322
+ setFileName(file.name);
323
+ setBusy(true);
324
+ try {
325
+ const res = await (0, _leaveRequestService.uploadLeaveFile)(api, file);
326
+ if (res?.Status === 'Success' && res?.data) {
327
+ setFilePath(res.data);
328
+ } else {
329
+ setFileName('');
330
+ setFilePath(undefined);
331
+ _reactNative.Alert.alert('Error', 'File upload failed');
332
+ }
333
+ } catch (error) {
334
+ setFileName('');
335
+ setFilePath(undefined);
336
+ _reactNative.Alert.alert('Error', String(error?.message ?? 'File upload failed'));
337
+ } finally {
338
+ setBusy(false);
339
+ setUploadOpen(false);
340
+ }
341
+ }, [api]);
342
+ const submit = (0, _react.useCallback)(async () => {
343
+ const missing = [];
344
+ if (!reason) missing.push('reason');
345
+ if (!leaveType?.value) missing.push('leave type');
346
+ if (!startDate) missing.push('start date');
347
+ if (!endDate) missing.push('end date');
348
+ if (missing.length) {
349
+ _reactNative.Alert.alert('Missing Fields', `Please fill: ${missing.join(', ')}`);
350
+ return;
351
+ }
352
+ setBusy(true);
353
+ try {
354
+ const res = await (0, _leaveRequestService.applyLeave)(api, {
355
+ reason,
356
+ leave_type: leaveType.value,
357
+ from_date: startDate,
358
+ to_date: endDate,
359
+ file: filePath
360
+ });
361
+ const response = res;
362
+ if (response?.Status && response.Status !== 'Success') {
363
+ _reactNative.Alert.alert('Failed', String(response?.msg ?? response?.Message ?? 'Request failed'));
364
+ return;
365
+ }
366
+ _reactNative.Alert.alert('Success', String(response?.msg ?? 'Leave applied successfully'));
367
+ onSuccess();
368
+ } catch (error) {
369
+ _reactNative.Alert.alert('Failed', String(error?.message ?? 'Could not apply leave'));
370
+ } finally {
371
+ setBusy(false);
372
+ }
373
+ }, [api, endDate, filePath, leaveType, onSuccess, reason, startDate]);
374
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
375
+ style: styles.root,
376
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
377
+ style: styles.header,
378
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
379
+ style: styles.headerTitle,
380
+ children: "Leave Request"
381
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
382
+ onPress: onCancel,
383
+ style: styles.secondaryBtn,
384
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
385
+ style: styles.secondaryBtnText,
386
+ children: "Back"
387
+ })
388
+ })]
389
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
390
+ contentContainerStyle: styles.form,
391
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
392
+ style: styles.formCard,
393
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
394
+ style: styles.field,
395
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
396
+ style: styles.label,
397
+ children: "Leave Reason"
398
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
399
+ value: reason,
400
+ onChangeText: setReason,
401
+ placeholder: "Enter the reason for leave",
402
+ placeholderTextColor: "#9CA3AF",
403
+ style: [styles.input, styles.textarea],
404
+ multiline: true
405
+ })]
406
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectModal, {
407
+ label: "Leave Type",
408
+ value: leaveType,
409
+ options: leaveTypes,
410
+ placeholder: "Select leave type",
411
+ onChange: setLeaveType
412
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
413
+ style: styles.field,
414
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
415
+ style: styles.label,
416
+ children: "Select Dates"
417
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
418
+ style: styles.toggleRow,
419
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
420
+ onPress: () => {
421
+ setDateMode('start');
422
+ setCalendarOpen(true);
423
+ },
424
+ style: [styles.toggleBtn, dateMode === 'start' ? styles.toggleBtnActive : null],
425
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
426
+ style: [styles.toggleBtnText, dateMode === 'start' ? styles.toggleBtnTextActive : null],
427
+ children: ["Start: ", startDate || 'Select']
428
+ })
429
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
430
+ onPress: () => {
431
+ setDateMode('end');
432
+ setCalendarOpen(true);
433
+ },
434
+ style: [styles.toggleBtn, dateMode === 'end' ? styles.toggleBtnActive : null],
435
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
436
+ style: [styles.toggleBtnText, dateMode === 'end' ? styles.toggleBtnTextActive : null],
437
+ children: ["End: ", endDate || 'Select']
438
+ })
439
+ })]
440
+ })]
441
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
442
+ style: styles.field,
443
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
444
+ style: styles.label,
445
+ children: "Attachment (Optional)"
446
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
447
+ onPress: () => setUploadOpen(true),
448
+ style: styles.uploadBtn,
449
+ disabled: busy,
450
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
451
+ style: styles.uploadBtnText,
452
+ children: filePath ? 'Change File' : 'Attach Document'
453
+ })
454
+ }), fileName ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
455
+ style: styles.fileNameText,
456
+ children: ["File Selected: ", fileName]
457
+ }) : null]
458
+ })]
459
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
460
+ onPress: () => submit().catch(() => {}),
461
+ style: styles.primaryBtn,
462
+ disabled: busy,
463
+ children: busy ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
464
+ color: "#FFFFFF"
465
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
466
+ style: styles.primaryBtnText,
467
+ children: "Apply Leave"
468
+ })
469
+ })]
470
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
471
+ visible: calendarOpen,
472
+ transparent: true,
473
+ animationType: "fade",
474
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
475
+ style: styles.modalOverlay,
476
+ onPress: () => setCalendarOpen(false),
477
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
478
+ style: styles.modalCard,
479
+ onPress: () => {},
480
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
481
+ style: styles.sheetHeader,
482
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
483
+ style: styles.modalTitle,
484
+ children: dateMode === 'start' ? 'Select Start Date' : 'Select End Date'
485
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
486
+ onPress: () => setCalendarOpen(false),
487
+ style: styles.secondaryBtn,
488
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
489
+ style: styles.secondaryBtnText,
490
+ children: "Close"
491
+ })
492
+ })]
493
+ }), Calendar ? /*#__PURE__*/(0, _jsxRuntime.jsx)(Calendar, {
494
+ minDate: todayYmd(),
495
+ markingType: "period",
496
+ markedDates: markedDates,
497
+ onDayPress: day => {
498
+ const next = String(day?.dateString ?? '');
499
+ if (!next) {
500
+ return;
501
+ }
502
+ if (dateMode === 'start') {
503
+ if (endDate && new Date(next).getTime() > new Date(endDate).getTime()) {
504
+ _reactNative.Alert.alert('Error', 'Start date must be less than end date');
505
+ return;
506
+ }
507
+ setStartDate(next);
508
+ if (!endDate) {
509
+ setEndDate(next);
510
+ }
511
+ } else {
512
+ if (startDate && new Date(next).getTime() < new Date(startDate).getTime()) {
513
+ _reactNative.Alert.alert('Error', 'Start date must be less than end date');
514
+ return;
515
+ }
516
+ setEndDate(next);
517
+ if (!startDate) {
518
+ setStartDate(next);
519
+ }
520
+ }
521
+ setCalendarOpen(false);
522
+ }
523
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_EmptyState.EmptyState, {
524
+ title: "Calendar missing",
525
+ message: "Install react-native-calendars to enable date picker"
526
+ })]
527
+ })
528
+ })
529
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
530
+ visible: uploadOpen,
531
+ transparent: true,
532
+ animationType: "fade",
533
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
534
+ style: styles.modalOverlay,
535
+ onPress: () => setUploadOpen(false),
536
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
537
+ style: styles.actionSheet,
538
+ onPress: () => {},
539
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
540
+ style: styles.actionSheetTitle,
541
+ children: "Select Attachment"
542
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
543
+ style: styles.actionRow,
544
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
545
+ onPress: () => pickCamera().catch(() => {}),
546
+ style: styles.actionBtn,
547
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
548
+ style: styles.actionBtnTitle,
549
+ children: "Camera"
550
+ })
551
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
552
+ onPress: () => pickDocument().catch(() => {}),
553
+ style: styles.actionBtn,
554
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
555
+ style: styles.actionBtnTitle,
556
+ children: "Document"
557
+ })
558
+ })]
559
+ })]
560
+ })
561
+ })
562
+ })]
563
+ });
564
+ }
565
+ function LeaveRequestScreen(props) {
566
+ const {
567
+ api
568
+ } = (0, _useERP.useERP)();
569
+ const [mode, setMode] = (0, _react.useState)('list');
570
+ const [tab, setTab] = (0, _react.useState)('approved');
571
+ const [requests, setRequests] = (0, _react.useState)([]);
572
+ const [leaveTypes, setLeaveTypes] = (0, _react.useState)([]);
573
+ const [page, setPage] = (0, _react.useState)(props.page ?? 1);
574
+ const [totalPages, setTotalPages] = (0, _react.useState)(1);
575
+ const [loading, setLoading] = (0, _react.useState)(true);
576
+ const [loadingMore, setLoadingMore] = (0, _react.useState)(false);
577
+ const [refreshing, setRefreshing] = (0, _react.useState)(false);
578
+ const perPage = props.perPage ?? 5;
579
+ const loadLeaveTypes = (0, _react.useCallback)(async () => {
580
+ try {
581
+ const res = await (0, _leaveRequestService.fetchLeaveTypes)(api);
582
+ setLeaveTypes(normalizeLeaveTypes(res));
583
+ } catch {}
584
+ }, [api]);
585
+ const loadPage = (0, _react.useCallback)(async (nextPage, replace = false) => {
586
+ if (replace) {
587
+ setRefreshing(true);
588
+ } else if (nextPage === (props.page ?? 1)) {
589
+ setLoading(true);
590
+ } else {
591
+ setLoadingMore(true);
592
+ }
593
+ try {
594
+ const res = await (0, _leaveRequestService.fetchLeaveRequests)(api, {
595
+ page: nextPage,
596
+ perPage
597
+ });
598
+ const list = normalizeLeaveItems(res);
599
+ const response = res;
600
+ const nextTotalPages = Number(response?.pagination?.total_pages ?? 1);
601
+ setTotalPages(Number.isFinite(nextTotalPages) && nextTotalPages > 0 ? nextTotalPages : 1);
602
+ setPage(nextPage);
603
+ setRequests(prev => replace || nextPage === (props.page ?? 1) ? list : [...prev, ...list]);
604
+ } catch (error) {
605
+ _reactNative.Alert.alert('Error', String(error?.message ?? 'Could not load leave requests'));
606
+ } finally {
607
+ setLoading(false);
608
+ setLoadingMore(false);
609
+ setRefreshing(false);
610
+ }
611
+ }, [api, perPage, props.page]);
612
+ (0, _react.useEffect)(() => {
613
+ loadLeaveTypes().catch(() => {});
614
+ loadPage(props.page ?? 1, true).catch(() => {});
615
+ }, [loadLeaveTypes, loadPage, props.page]);
616
+ const approvedRequests = (0, _react.useMemo)(() => requests.filter(item => String(item?.status ?? '').toLowerCase() === 'approved'), [requests]);
617
+ const pendingRequests = (0, _react.useMemo)(() => requests.filter(item => String(item?.status ?? '').toLowerCase() !== 'approved'), [requests]);
618
+ const visibleRequests = tab === 'approved' ? approvedRequests : pendingRequests;
619
+ if (mode === 'apply') {
620
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(ApplyLeaveView, {
621
+ leaveTypes: leaveTypes,
622
+ onCancel: () => setMode('list'),
623
+ onSuccess: () => {
624
+ setMode('list');
625
+ loadPage(props.page ?? 1, true).catch(() => {});
626
+ }
627
+ });
628
+ }
629
+ if (loading) {
630
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
631
+ style: styles.root,
632
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
633
+ style: styles.header,
634
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
635
+ style: styles.headerTitle,
636
+ children: "Leaves"
637
+ })
638
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(LeaveRequestSkeleton, {})]
639
+ });
640
+ }
641
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.SafeAreaView, {
642
+ style: styles.root,
643
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
644
+ style: styles.header,
645
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
646
+ style: styles.headerTitle,
647
+ children: "Leaves"
648
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
649
+ onPress: () => setMode('apply'),
650
+ style: styles.primaryBtnSmall,
651
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
652
+ style: styles.primaryBtnSmallText,
653
+ children: "Apply"
654
+ })
655
+ })]
656
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
657
+ style: styles.tabRow,
658
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
659
+ onPress: () => setTab('approved'),
660
+ style: [styles.tabBtn, tab === 'approved' ? styles.tabBtnActive : null],
661
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
662
+ style: [styles.tabBtnText, tab === 'approved' ? styles.tabBtnTextActive : null],
663
+ children: "Approved"
664
+ })
665
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
666
+ onPress: () => setTab('pending'),
667
+ style: [styles.tabBtn, tab === 'pending' ? styles.tabBtnActive : null],
668
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
669
+ style: [styles.tabBtnText, tab === 'pending' ? styles.tabBtnTextActive : null],
670
+ children: "Pending / Declined"
671
+ })
672
+ })]
673
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
674
+ data: visibleRequests,
675
+ keyExtractor: (item, index) => String(item?.id ?? index),
676
+ renderItem: ({
677
+ item
678
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(LeaveRequestCard, {
679
+ item: item
680
+ }),
681
+ contentContainerStyle: styles.listContent,
682
+ onEndReached: () => {
683
+ if (!loadingMore && page < totalPages) {
684
+ loadPage(page + 1).catch(() => {});
685
+ }
686
+ },
687
+ onEndReachedThreshold: 0.4,
688
+ onRefresh: () => {
689
+ loadPage(props.page ?? 1, true).catch(() => {});
690
+ },
691
+ refreshing: refreshing,
692
+ ListFooterComponent: loadingMore ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
693
+ style: {
694
+ marginVertical: 16
695
+ },
696
+ color: "#2563EB"
697
+ }) : null,
698
+ ListEmptyComponent: /*#__PURE__*/(0, _jsxRuntime.jsx)(_EmptyState.EmptyState, {
699
+ title: "No Data Found"
700
+ })
701
+ })]
702
+ });
703
+ }
704
+ const styles = _reactNative.StyleSheet.create({
705
+ root: {
706
+ flex: 1,
707
+ backgroundColor: '#FAFAFA'
708
+ },
709
+ header: {
710
+ paddingHorizontal: 16,
711
+ paddingVertical: 12,
712
+ borderBottomWidth: 1,
713
+ borderBottomColor: '#E5E7EB',
714
+ backgroundColor: '#FFFFFF',
715
+ flexDirection: 'row',
716
+ justifyContent: 'space-between',
717
+ alignItems: 'center'
718
+ },
719
+ headerTitle: {
720
+ fontSize: 18,
721
+ fontWeight: '700',
722
+ color: '#111827'
723
+ },
724
+ primaryBtnSmall: {
725
+ paddingHorizontal: 14,
726
+ paddingVertical: 10,
727
+ borderRadius: 10,
728
+ backgroundColor: '#2563EB'
729
+ },
730
+ primaryBtnSmallText: {
731
+ color: '#FFFFFF',
732
+ fontWeight: '700'
733
+ },
734
+ tabRow: {
735
+ flexDirection: 'row',
736
+ backgroundColor: '#FFFFFF',
737
+ paddingVertical: 12,
738
+ paddingHorizontal: 16,
739
+ gap: 12,
740
+ borderBottomWidth: 1,
741
+ borderBottomColor: '#E5E7EB'
742
+ },
743
+ tabBtn: {
744
+ flex: 1,
745
+ paddingVertical: 10,
746
+ alignItems: 'center',
747
+ backgroundColor: '#F5F5F5',
748
+ borderRadius: 8,
749
+ borderWidth: 1,
750
+ borderColor: '#E0E0E0'
751
+ },
752
+ tabBtnActive: {
753
+ backgroundColor: '#2563EB',
754
+ borderColor: '#2563EB'
755
+ },
756
+ tabBtnText: {
757
+ color: '#757575',
758
+ fontWeight: '600',
759
+ fontSize: 15
760
+ },
761
+ tabBtnTextActive: {
762
+ color: '#FFFFFF'
763
+ },
764
+ listContent: {
765
+ paddingVertical: 8
766
+ },
767
+ requestCard: {
768
+ backgroundColor: '#FFFFFF',
769
+ borderRadius: 12,
770
+ marginVertical: 8,
771
+ marginHorizontal: 16,
772
+ padding: 16,
773
+ borderWidth: 1,
774
+ borderColor: '#F0F0F0'
775
+ },
776
+ requestTopRow: {
777
+ flexDirection: 'row',
778
+ justifyContent: 'space-between',
779
+ alignItems: 'flex-start'
780
+ },
781
+ requestTitle: {
782
+ fontSize: 16,
783
+ fontWeight: '700',
784
+ color: '#333333',
785
+ marginBottom: 4
786
+ },
787
+ requestType: {
788
+ fontSize: 14,
789
+ color: '#2563EB',
790
+ fontWeight: '500',
791
+ marginBottom: 8
792
+ },
793
+ requestDate: {
794
+ fontSize: 14,
795
+ color: '#666666',
796
+ fontWeight: '500'
797
+ },
798
+ statusChip: {
799
+ paddingHorizontal: 12,
800
+ paddingVertical: 6,
801
+ borderRadius: 20
802
+ },
803
+ statusChipText: {
804
+ fontWeight: '600',
805
+ fontSize: 12,
806
+ textTransform: 'capitalize'
807
+ },
808
+ skeletonWrap: {
809
+ padding: 16,
810
+ gap: 12
811
+ },
812
+ skeletonCard: {
813
+ height: 96,
814
+ borderRadius: 12,
815
+ backgroundColor: '#E5E7EB'
816
+ },
817
+ form: {
818
+ padding: 16,
819
+ gap: 14
820
+ },
821
+ formCard: {
822
+ backgroundColor: '#FFFFFF',
823
+ borderRadius: 16,
824
+ padding: 16,
825
+ borderWidth: 1,
826
+ borderColor: '#E5E7EB',
827
+ gap: 12
828
+ },
829
+ field: {
830
+ gap: 6
831
+ },
832
+ label: {
833
+ fontSize: 12,
834
+ color: '#6B7280',
835
+ fontWeight: '600'
836
+ },
837
+ input: {
838
+ borderWidth: 1,
839
+ borderColor: '#E5E7EB',
840
+ borderRadius: 12,
841
+ paddingHorizontal: 12,
842
+ paddingVertical: 10,
843
+ color: '#111827',
844
+ backgroundColor: '#FFFFFF'
845
+ },
846
+ textarea: {
847
+ minHeight: 90,
848
+ textAlignVertical: 'top'
849
+ },
850
+ select: {
851
+ borderWidth: 1,
852
+ borderColor: '#E5E7EB',
853
+ borderRadius: 12,
854
+ paddingHorizontal: 12,
855
+ paddingVertical: 12,
856
+ backgroundColor: '#FFFFFF'
857
+ },
858
+ selectText: {
859
+ fontSize: 14,
860
+ color: '#111827'
861
+ },
862
+ toggleRow: {
863
+ flexDirection: 'row',
864
+ gap: 10
865
+ },
866
+ toggleBtn: {
867
+ flex: 1,
868
+ paddingVertical: 12,
869
+ borderRadius: 12,
870
+ backgroundColor: '#F3F4F6',
871
+ alignItems: 'center'
872
+ },
873
+ toggleBtnActive: {
874
+ backgroundColor: '#2563EB'
875
+ },
876
+ toggleBtnText: {
877
+ fontSize: 13,
878
+ fontWeight: '600',
879
+ color: '#374151'
880
+ },
881
+ toggleBtnTextActive: {
882
+ color: '#FFFFFF'
883
+ },
884
+ uploadBtn: {
885
+ paddingVertical: 12,
886
+ borderRadius: 12,
887
+ backgroundColor: '#EFF6FF',
888
+ borderWidth: 1,
889
+ borderColor: '#93C5FD',
890
+ alignItems: 'center'
891
+ },
892
+ uploadBtnText: {
893
+ color: '#1D4ED8',
894
+ fontWeight: '700'
895
+ },
896
+ fileNameText: {
897
+ fontSize: 12,
898
+ color: '#6B7280'
899
+ },
900
+ primaryBtn: {
901
+ paddingVertical: 14,
902
+ borderRadius: 12,
903
+ backgroundColor: '#2563EB',
904
+ alignItems: 'center',
905
+ justifyContent: 'center'
906
+ },
907
+ primaryBtnText: {
908
+ color: '#FFFFFF',
909
+ fontWeight: '800',
910
+ fontSize: 15
911
+ },
912
+ secondaryBtn: {
913
+ paddingHorizontal: 12,
914
+ paddingVertical: 8,
915
+ borderRadius: 10,
916
+ backgroundColor: '#F3F4F6'
917
+ },
918
+ secondaryBtnText: {
919
+ color: '#111827',
920
+ fontWeight: '700'
921
+ },
922
+ modalOverlay: {
923
+ flex: 1,
924
+ backgroundColor: 'rgba(0,0,0,0.45)',
925
+ justifyContent: 'center',
926
+ padding: 16
927
+ },
928
+ modalCard: {
929
+ backgroundColor: '#FFFFFF',
930
+ borderRadius: 16,
931
+ overflow: 'hidden',
932
+ maxHeight: '80%'
933
+ },
934
+ modalTitle: {
935
+ fontSize: 16,
936
+ fontWeight: '700',
937
+ color: '#111827'
938
+ },
939
+ optionRow: {
940
+ paddingHorizontal: 16,
941
+ paddingVertical: 12
942
+ },
943
+ optionText: {
944
+ fontSize: 14,
945
+ color: '#111827'
946
+ },
947
+ optionEmpty: {
948
+ paddingHorizontal: 16,
949
+ paddingVertical: 12,
950
+ color: '#6B7280'
951
+ },
952
+ sheetHeader: {
953
+ paddingHorizontal: 16,
954
+ paddingVertical: 12,
955
+ borderBottomWidth: 1,
956
+ borderBottomColor: '#E5E7EB',
957
+ flexDirection: 'row',
958
+ justifyContent: 'space-between',
959
+ alignItems: 'center'
960
+ },
961
+ actionSheet: {
962
+ backgroundColor: '#FFFFFF',
963
+ borderRadius: 16,
964
+ padding: 20
965
+ },
966
+ actionSheetTitle: {
967
+ fontSize: 18,
968
+ fontWeight: '700',
969
+ color: '#111827',
970
+ textAlign: 'center',
971
+ marginBottom: 20
972
+ },
973
+ actionRow: {
974
+ flexDirection: 'row',
975
+ gap: 12
976
+ },
977
+ actionBtn: {
978
+ flex: 1,
979
+ borderWidth: 1,
980
+ borderColor: '#DBEAFE',
981
+ backgroundColor: '#EFF6FF',
982
+ borderRadius: 14,
983
+ paddingVertical: 18,
984
+ alignItems: 'center'
985
+ },
986
+ actionBtnTitle: {
987
+ fontSize: 14,
988
+ fontWeight: '700',
989
+ color: '#1D4ED8'
990
+ }
991
+ });