oceanhelm 0.0.11 → 0.0.12
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.
- package/dist/oceanhelm.es.js +2124 -1237
- package/dist/oceanhelm.es.js.map +1 -1
- package/dist/oceanhelm.umd.js +1 -1
- package/dist/oceanhelm.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/ActivityLogs.vue +319 -330
- package/src/components/ConfigurableSidebar.vue +55 -8
- package/src/components/CrewManagement.vue +686 -36
- package/src/components/Reports.vue +1116 -279
- package/src/components/RequisitionSystem.vue +97 -67
- package/src/utils/sidebarConfig.js +48 -25
|
@@ -1,32 +1,99 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="reports-container">
|
|
3
|
+
|
|
4
|
+
<!-- ── Missing Reports Alert Banner ── -->
|
|
5
|
+
<transition name="slide-down">
|
|
6
|
+
<div class="missing-banner" v-if="pendingContext || missingReports.length > 0">
|
|
7
|
+
<div class="missing-banner-inner">
|
|
8
|
+
<div class="missing-icon">
|
|
9
|
+
<i class="bi bi-exclamation-triangle-fill"></i>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="missing-content">
|
|
12
|
+
<strong v-if="pendingContext">
|
|
13
|
+
Action Required — Report for
|
|
14
|
+
<span class="ref-chip">{{ pendingContext.entity_ref }}</span>
|
|
15
|
+
must be submitted
|
|
16
|
+
</strong>
|
|
17
|
+
<strong v-else>
|
|
18
|
+
{{ missingReports.length }} report{{ missingReports.length > 1 ? 's' : '' }} awaiting
|
|
19
|
+
submission
|
|
20
|
+
</strong>
|
|
21
|
+
<p v-if="pendingContext">{{ pendingContext.title }}</p>
|
|
22
|
+
</div>
|
|
23
|
+
<button class="btn btn-warning btn-sm fw-bold" @click="openReportForm">
|
|
24
|
+
<i class="bi bi-pencil-square me-1"></i> Submit Report
|
|
25
|
+
</button>
|
|
26
|
+
<button class="btn-dismiss" @click="dismissBanner" title="Dismiss">
|
|
27
|
+
<i class="bi bi-x-lg"></i>
|
|
28
|
+
</button>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</transition>
|
|
32
|
+
|
|
33
|
+
<!-- ── Page Header ── -->
|
|
3
34
|
<div class="page-header d-flex justify-content-between align-items-center">
|
|
4
|
-
<
|
|
5
|
-
|
|
35
|
+
<div>
|
|
36
|
+
<h4 class="page-title">Reports</h4>
|
|
37
|
+
<p class="page-subtitle">Document Management & QHSE Reports</p>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="header-actions">
|
|
40
|
+
<button class="btn btn-outline-primary me-2" @click="openReportForm">
|
|
41
|
+
<i class="bi bi-file-earmark-plus"></i> New Report
|
|
42
|
+
</button>
|
|
6
43
|
<button class="btn btn-primary" @click="showNewFolderModal = true">
|
|
7
44
|
<i class="bi bi-folder-plus"></i> New Folder
|
|
8
45
|
</button>
|
|
9
46
|
</div>
|
|
10
47
|
</div>
|
|
11
48
|
|
|
12
|
-
<!--
|
|
49
|
+
<!-- ── Submitted Reports Section ── -->
|
|
50
|
+
<div class="submitted-section" v-if="linkedReports.length > 0">
|
|
51
|
+
<div class="section-label">
|
|
52
|
+
<i class="bi bi-file-earmark-check-fill text-success"></i>
|
|
53
|
+
Submitted QHSE Reports
|
|
54
|
+
</div>
|
|
55
|
+
<div class="report-cards">
|
|
56
|
+
<div v-for="report in linkedReports" :key="report.id" :ref="'report-' + report.entity_ref"
|
|
57
|
+
:class="['report-card', { 'report-card--highlighted': highlightedRef === report.entity_ref }]"
|
|
58
|
+
@click="viewReport(report)">
|
|
59
|
+
<div class="rc-badge" :class="report.entity_type">
|
|
60
|
+
{{ report.entity_type === 'drill' ? 'DRILL' : 'INC' }}
|
|
61
|
+
</div>
|
|
62
|
+
<div class="rc-body">
|
|
63
|
+
<strong class="rc-ref">{{ report.entity_ref }}</strong>
|
|
64
|
+
<span class="rc-title">{{ report.title }}</span>
|
|
65
|
+
<span class="rc-meta">
|
|
66
|
+
<i class="bi bi-person-fill me-1"></i>{{ report.submittedBy }}
|
|
67
|
+
·
|
|
68
|
+
<i class="bi bi-calendar3 me-1"></i>{{ formatDate(report.submittedAt) }}
|
|
69
|
+
</span>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="rc-folder" v-if="report.folderName">
|
|
72
|
+
<i class="bi bi-folder-fill me-1"></i>{{ report.folderName }}
|
|
73
|
+
</div>
|
|
74
|
+
<i class="bi bi-chevron-right rc-arrow"></i>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<!-- ── Folders Section ── -->
|
|
80
|
+
<div class="section-label mt-4" v-if="folders.length > 0 || linkedReports.length === 0">
|
|
81
|
+
<i class="bi bi-folder-fill text-warning"></i> Document Folders
|
|
82
|
+
</div>
|
|
13
83
|
<div class="folders-section" v-if="folders.length > 0">
|
|
14
|
-
<div class="folder-card"
|
|
15
|
-
|
|
16
|
-
:key="folder.id"
|
|
17
|
-
@mouseenter="showPreview(folder.id)"
|
|
18
|
-
@mouseleave="hidePreview">
|
|
84
|
+
<div class="folder-card" v-for="folder in folders" :key="folder.id" @mouseenter="showPreview(folder.id)"
|
|
85
|
+
@mouseleave="hidePreview">
|
|
19
86
|
<div class="folder-content" @click="toggleFolder(folder.id)">
|
|
20
87
|
<i class="bi bi-folder-fill text-warning" style="font-size: 2.5rem;"></i>
|
|
21
88
|
<div class="folder-name">{{ folder.name }}</div>
|
|
22
89
|
<small class="text-muted">{{ folder.files.length }} file(s)</small>
|
|
23
90
|
</div>
|
|
24
|
-
|
|
25
91
|
<div class="folder-actions-overlay">
|
|
26
92
|
<button class="btn btn-sm btn-icon" @click.stop="selectFolder(folder)" title="Add Files">
|
|
27
93
|
<i class="bi bi-file-earmark-plus"></i>
|
|
28
94
|
</button>
|
|
29
|
-
<button class="btn btn-sm btn-icon" @click.stop="deleteFolder(folder.id)"
|
|
95
|
+
<button class="btn btn-sm btn-icon btn-icon-danger" @click.stop="deleteFolder(folder.id)"
|
|
96
|
+
title="Delete Folder">
|
|
30
97
|
<i class="bi bi-trash"></i>
|
|
31
98
|
</button>
|
|
32
99
|
</div>
|
|
@@ -34,31 +101,31 @@
|
|
|
34
101
|
</div>
|
|
35
102
|
|
|
36
103
|
<!-- Empty State -->
|
|
37
|
-
<div class="empty-state text-center" v-
|
|
104
|
+
<div class="empty-state text-center" v-if="folders.length === 0 && linkedReports.length === 0">
|
|
38
105
|
<i class="bi bi-folder2-open" style="font-size: 4rem; color: #ccc;"></i>
|
|
39
|
-
<h5 class="mt-3 text-muted">No folders yet</h5>
|
|
40
|
-
<p class="text-muted">
|
|
106
|
+
<h5 class="mt-3 text-muted">No reports or folders yet</h5>
|
|
107
|
+
<p class="text-muted">Submit a report or create a folder to get started</p>
|
|
41
108
|
</div>
|
|
42
109
|
|
|
43
|
-
<!-- Expanded Folder Modal
|
|
110
|
+
<!-- ── Expanded Folder Modal ── -->
|
|
44
111
|
<div class="folder-modal-overlay" v-if="expandedFolders.length > 0" @click="closeAllFolders">
|
|
45
112
|
<div class="folder-expanded" @click.stop>
|
|
46
113
|
<div class="expanded-header">
|
|
47
114
|
<strong>{{ getExpandedFolder().name }}</strong>
|
|
48
|
-
<button class="btn
|
|
115
|
+
<button class="btn-close-expanded" @click="closeAllFolders">
|
|
49
116
|
<i class="bi bi-x-lg"></i>
|
|
50
117
|
</button>
|
|
51
118
|
</div>
|
|
52
|
-
|
|
53
119
|
<div class="files-list" v-if="getExpandedFolder().files.length > 0">
|
|
54
|
-
<div class="file-item d-flex justify-content-between align-items-center"
|
|
55
|
-
|
|
120
|
+
<div class="file-item d-flex justify-content-between align-items-center"
|
|
121
|
+
v-for="file in getExpandedFolder().files" :key="file.id">
|
|
56
122
|
<div class="file-info d-flex align-items-center">
|
|
57
123
|
<i class="bi bi-file-earmark-text me-2"></i>
|
|
58
124
|
<span>{{ file.name }}</span>
|
|
59
125
|
<small class="text-muted ms-2">({{ formatFileSize(file.size) }})</small>
|
|
60
126
|
</div>
|
|
61
|
-
<button class="btn btn-sm btn-outline-danger"
|
|
127
|
+
<button class="btn btn-sm btn-outline-danger"
|
|
128
|
+
@click="removeFile(getExpandedFolder().id, file.id)">
|
|
62
129
|
<i class="bi bi-x-circle"></i>
|
|
63
130
|
</button>
|
|
64
131
|
</div>
|
|
@@ -73,29 +140,24 @@
|
|
|
73
140
|
</div>
|
|
74
141
|
</div>
|
|
75
142
|
|
|
76
|
-
<!-- New Folder Modal -->
|
|
143
|
+
<!-- ── New Folder Modal ── -->
|
|
77
144
|
<div class="modal" :class="{ 'show': showNewFolderModal }" @click.self="showNewFolderModal = false">
|
|
78
145
|
<div class="modal-dialog modal-dialog-centered">
|
|
79
146
|
<div class="modal-content">
|
|
80
147
|
<div class="modal-header">
|
|
81
|
-
<h5 class="modal-title">Create New Folder</h5>
|
|
148
|
+
<h5 class="modal-title"><i class="bi bi-folder-plus me-2"></i>Create New Folder</h5>
|
|
82
149
|
<button type="button" class="btn-close" @click="showNewFolderModal = false"></button>
|
|
83
150
|
</div>
|
|
84
151
|
<div class="modal-body">
|
|
85
|
-
<
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
type="text"
|
|
89
|
-
class="form-control"
|
|
90
|
-
v-model="newFolderName"
|
|
91
|
-
@keyup.enter="createFolder"
|
|
92
|
-
placeholder="Enter folder name"
|
|
93
|
-
/>
|
|
94
|
-
</div>
|
|
152
|
+
<label class="form-label">Folder Name</label>
|
|
153
|
+
<input type="text" class="form-control" v-model="newFolderName" @keyup.enter="createFolder"
|
|
154
|
+
placeholder="e.g., Q1 2025 Drills" />
|
|
95
155
|
</div>
|
|
96
156
|
<div class="modal-footer">
|
|
97
|
-
<button type="button" class="btn btn-secondary"
|
|
98
|
-
|
|
157
|
+
<button type="button" class="btn btn-secondary"
|
|
158
|
+
@click="showNewFolderModal = false">Cancel</button>
|
|
159
|
+
<button type="button" class="btn btn-primary" @click="createFolder"
|
|
160
|
+
:disabled="!newFolderName.trim()">
|
|
99
161
|
Create Folder
|
|
100
162
|
</button>
|
|
101
163
|
</div>
|
|
@@ -103,87 +165,396 @@
|
|
|
103
165
|
</div>
|
|
104
166
|
</div>
|
|
105
167
|
|
|
106
|
-
<!--
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
168
|
+
<!-- ── Submit Report Modal ── -->
|
|
169
|
+
<div class="modal" :class="{ 'show': showReportModal }" @click.self="closeReportForm">
|
|
170
|
+
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
171
|
+
<div class="modal-content">
|
|
172
|
+
<div class="modal-header rpt-modal-header">
|
|
173
|
+
<h5 class="modal-title">
|
|
174
|
+
<i class="bi bi-file-earmark-text me-2"></i>Submit QHSE Report
|
|
175
|
+
</h5>
|
|
176
|
+
<button type="button" class="btn-close btn-close-white" @click="closeReportForm"></button>
|
|
177
|
+
</div>
|
|
178
|
+
<div class="modal-body">
|
|
179
|
+
|
|
180
|
+
<!-- Link to entity -->
|
|
181
|
+
<div class="rpt-entity-box" v-if="reportForm.entity_ref || missingReports.length > 0">
|
|
182
|
+
<label class="form-label fw-semibold">
|
|
183
|
+
<i class="bi bi-link-45deg me-1"></i> Link to Drill / Incident
|
|
184
|
+
</label>
|
|
185
|
+
|
|
186
|
+
<!-- Pre-filled from navigation -->
|
|
187
|
+
<div class="prefilled-entity" v-if="reportForm.entity_ref && reportForm.entity_type">
|
|
188
|
+
<span class="entity-chip" :class="reportForm.entity_type">
|
|
189
|
+
{{ reportForm.entity_type === 'drill' ? 'DRILL' : 'INCIDENT' }}
|
|
190
|
+
</span>
|
|
191
|
+
<strong>{{ reportForm.entity_ref }}</strong>
|
|
192
|
+
<span class="text-muted ms-2">{{ reportForm.entity_label }}</span>
|
|
193
|
+
<button class="btn btn-link btn-sm text-danger ms-auto" @click="clearEntityLink">
|
|
194
|
+
<i class="bi bi-x-circle"></i> Clear
|
|
195
|
+
</button>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
<!-- Dropdown when no pre-fill -->
|
|
199
|
+
<div v-else>
|
|
200
|
+
<select class="form-control" v-model="reportForm.selectedMissing"
|
|
201
|
+
@change="applyMissingSelection">
|
|
202
|
+
<option value="">— Select a missing report (optional) —</option>
|
|
203
|
+
<optgroup label="Missing Drill Reports">
|
|
204
|
+
<option v-for="r in missingReports.filter(x => x.entity_type === 'drill')"
|
|
205
|
+
:key="r.entity_id" :value="JSON.stringify(r)">
|
|
206
|
+
{{ r.entity_ref }} — {{ r.label }}
|
|
207
|
+
</option>
|
|
208
|
+
</optgroup>
|
|
209
|
+
<optgroup label="Missing Incident Reports">
|
|
210
|
+
<option v-for="r in missingReports.filter(x => x.entity_type === 'incident')"
|
|
211
|
+
:key="r.entity_id" :value="JSON.stringify(r)">
|
|
212
|
+
{{ r.entity_ref }} — {{ r.label }}
|
|
213
|
+
</option>
|
|
214
|
+
</optgroup>
|
|
215
|
+
</select>
|
|
216
|
+
<small class="text-muted">Linking a report here will mark the drill/incident as
|
|
217
|
+
reported.</small>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<!-- Report Fields -->
|
|
222
|
+
<div class="input-row mt-3">
|
|
223
|
+
<div class="form-group">
|
|
224
|
+
<label class="form-label fw-semibold">Report Title *</label>
|
|
225
|
+
<input type="text" class="form-control" v-model="reportForm.title"
|
|
226
|
+
:placeholder="reportForm.entity_ref ? `Report — ${reportForm.entity_ref}` : 'Enter report title'" />
|
|
227
|
+
</div>
|
|
228
|
+
<div class="form-group">
|
|
229
|
+
<label class="form-label fw-semibold">Submitted By *</label>
|
|
230
|
+
<input type="text" class="form-control" v-model="reportForm.submittedBy"
|
|
231
|
+
placeholder="Your name" />
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<div class="input-row">
|
|
236
|
+
<div class="form-group">
|
|
237
|
+
<label class="form-label fw-semibold">Save to Folder</label>
|
|
238
|
+
<select class="form-control" v-model="reportForm.folderId">
|
|
239
|
+
<option value="">— No folder —</option>
|
|
240
|
+
<option v-for="f in folders" :key="f.id" :value="f.id">{{ f.name }}</option>
|
|
241
|
+
</select>
|
|
242
|
+
</div>
|
|
243
|
+
<div class="form-group">
|
|
244
|
+
<label class="form-label fw-semibold">Date</label>
|
|
245
|
+
<input type="date" class="form-control" v-model="reportForm.date" />
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<div class="form-group">
|
|
250
|
+
<label class="form-label fw-semibold">Findings / Summary *</label>
|
|
251
|
+
<textarea class="form-control" rows="4" v-model="reportForm.findings"
|
|
252
|
+
placeholder="Describe findings, observations, and outcomes..."></textarea>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<div class="form-group">
|
|
256
|
+
<label class="form-label fw-semibold">Corrective Actions</label>
|
|
257
|
+
<textarea class="form-control" rows="3" v-model="reportForm.correctiveActions"
|
|
258
|
+
placeholder="List any corrective actions required or taken..."></textarea>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<div class="form-group">
|
|
262
|
+
<label class="form-label fw-semibold">Attach Files</label>
|
|
263
|
+
<div class="file-drop-zone" @click="$refs.reportFileInput.click()" @dragover.prevent
|
|
264
|
+
@drop.prevent="handleReportFileDrop">
|
|
265
|
+
<i class="bi bi-cloud-upload" style="font-size:2rem;color:#6c757d;"></i>
|
|
266
|
+
<p class="mb-0 mt-2 text-muted">Click or drag files here</p>
|
|
267
|
+
<div class="attached-files" v-if="reportForm.files.length > 0">
|
|
268
|
+
<span class="attached-chip" v-for="(f, i) in reportForm.files" :key="i">
|
|
269
|
+
<i class="bi bi-paperclip"></i> {{ f.name }}
|
|
270
|
+
<button @click.stop="removeReportFile(i)"><i class="bi bi-x"></i></button>
|
|
271
|
+
</span>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
<input ref="reportFileInput" type="file" multiple style="display:none"
|
|
275
|
+
@change="handleReportFileAttach" />
|
|
276
|
+
</div>
|
|
277
|
+
|
|
278
|
+
</div>
|
|
279
|
+
<div class="modal-footer">
|
|
280
|
+
<button type="button" class="btn btn-secondary" @click="closeReportForm">Cancel</button>
|
|
281
|
+
<button type="button" class="btn btn-success" @click="submitReport"
|
|
282
|
+
:disabled="!reportForm.title || !reportForm.submittedBy || !reportForm.findings">
|
|
283
|
+
<i class="bi bi-check-circle me-1"></i> Submit Report
|
|
284
|
+
</button>
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
<!-- ── Report Viewer Modal ── -->
|
|
291
|
+
<div class="modal" :class="{ 'show': showViewModal }" @click.self="showViewModal = false">
|
|
292
|
+
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
293
|
+
<div class="modal-content" v-if="viewingReport">
|
|
294
|
+
<div class="modal-header rpt-modal-header">
|
|
295
|
+
<div>
|
|
296
|
+
<span class="entity-chip me-2" :class="viewingReport.entity_type">
|
|
297
|
+
{{ viewingReport.entity_type === 'drill' ? 'DRILL' : 'INCIDENT' }}
|
|
298
|
+
</span>
|
|
299
|
+
<strong style="color:#fff">{{ viewingReport.title }}</strong>
|
|
300
|
+
</div>
|
|
301
|
+
<button type="button" class="btn-close btn-close-white" @click="showViewModal = false"></button>
|
|
302
|
+
</div>
|
|
303
|
+
<div class="modal-body">
|
|
304
|
+
<div class="view-grid">
|
|
305
|
+
<div class="view-item"><label>Reference</label><span class="ref-mono">{{
|
|
306
|
+
viewingReport.entity_ref }}</span></div>
|
|
307
|
+
<div class="view-item"><label>Type</label><span>{{ viewingReport.entity_type }}</span></div>
|
|
308
|
+
<div class="view-item"><label>Submitted By</label><span>{{ viewingReport.submittedBy
|
|
309
|
+
}}</span></div>
|
|
310
|
+
<div class="view-item"><label>Date</label><span>{{ viewingReport.date }}</span></div>
|
|
311
|
+
<div class="view-item"><label>Folder</label><span>{{ viewingReport.folderName || '—'
|
|
312
|
+
}}</span></div>
|
|
313
|
+
<div class="view-item"><label>Submitted At</label><span>{{
|
|
314
|
+
formatDate(viewingReport.submittedAt) }}</span></div>
|
|
315
|
+
</div>
|
|
316
|
+
<div class="view-section">
|
|
317
|
+
<label>Findings / Summary</label>
|
|
318
|
+
<div class="view-text">{{ viewingReport.findings }}</div>
|
|
319
|
+
</div>
|
|
320
|
+
<div class="view-section" v-if="viewingReport.correctiveActions">
|
|
321
|
+
<label>Corrective Actions</label>
|
|
322
|
+
<div class="view-text">{{ viewingReport.correctiveActions }}</div>
|
|
323
|
+
</div>
|
|
324
|
+
<div class="view-section" v-if="viewingReport.files && viewingReport.files.length > 0">
|
|
325
|
+
<label>Attachments</label>
|
|
326
|
+
<div class="attached-files mt-1">
|
|
327
|
+
<span class="attached-chip" v-for="(f, i) in viewingReport.files" :key="i">
|
|
328
|
+
<i class="bi bi-paperclip"></i> {{ f.name }}
|
|
329
|
+
</span>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
<div class="modal-footer">
|
|
334
|
+
<button class="btn btn-secondary" @click="showViewModal = false">Close</button>
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
<!-- Hidden file input for folders -->
|
|
341
|
+
<input ref="fileInput" type="file" multiple style="display: none;" @change="handleFileSelection" />
|
|
114
342
|
</div>
|
|
115
343
|
</template>
|
|
116
344
|
|
|
117
345
|
<script>
|
|
118
346
|
export default {
|
|
119
347
|
name: 'Reports',
|
|
120
|
-
|
|
348
|
+
|
|
349
|
+
props: {
|
|
350
|
+
// Passed from ReportsView when navigating from HSE
|
|
351
|
+
pendingContext: {
|
|
352
|
+
type: Object,
|
|
353
|
+
default: null
|
|
354
|
+
// { entity_type, entity_id, entity_ref, title }
|
|
355
|
+
},
|
|
356
|
+
// All missing reports (drills + incidents without reports)
|
|
357
|
+
missingReports: {
|
|
358
|
+
type: Array,
|
|
359
|
+
default: () => []
|
|
360
|
+
// [{ entity_type, entity_id, entity_ref, label }]
|
|
361
|
+
},
|
|
362
|
+
// Ref to scroll to / highlight (from ?ref= query param)
|
|
363
|
+
highlightRef: {
|
|
364
|
+
type: String,
|
|
365
|
+
default: null
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
|
|
369
|
+
emits: [
|
|
370
|
+
'folder-created', 'files-added', 'file-removed', 'folder-deleted',
|
|
371
|
+
'data-updated', 'report-submitted', 'report-viewed'
|
|
372
|
+
],
|
|
373
|
+
|
|
121
374
|
data() {
|
|
122
375
|
return {
|
|
123
376
|
folders: [],
|
|
377
|
+
linkedReports: [], // submitted QHSE reports
|
|
124
378
|
showNewFolderModal: false,
|
|
125
379
|
newFolderName: '',
|
|
126
380
|
selectedFolder: null,
|
|
127
381
|
expandedFolders: [],
|
|
128
|
-
previewFolder: null
|
|
129
|
-
|
|
382
|
+
previewFolder: null,
|
|
383
|
+
showReportModal: false,
|
|
384
|
+
showViewModal: false,
|
|
385
|
+
viewingReport: null,
|
|
386
|
+
highlightedRef: null,
|
|
387
|
+
bannerDismissed: false,
|
|
388
|
+
reportForm: {
|
|
389
|
+
title: '',
|
|
390
|
+
submittedBy: '',
|
|
391
|
+
folderId: '',
|
|
392
|
+
date: new Date().toISOString().split('T')[0],
|
|
393
|
+
findings: '',
|
|
394
|
+
correctiveActions: '',
|
|
395
|
+
files: [],
|
|
396
|
+
entity_type: '',
|
|
397
|
+
entity_id: '',
|
|
398
|
+
entity_ref: '',
|
|
399
|
+
entity_label: '',
|
|
400
|
+
selectedMissing: '',
|
|
401
|
+
}
|
|
402
|
+
};
|
|
130
403
|
},
|
|
131
404
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
405
|
+
watch: {
|
|
406
|
+
pendingContext: {
|
|
407
|
+
immediate: true,
|
|
408
|
+
handler(ctx) {
|
|
409
|
+
if (ctx) {
|
|
410
|
+
this.reportForm.entity_type = ctx.entity_type;
|
|
411
|
+
this.reportForm.entity_id = ctx.entity_id;
|
|
412
|
+
this.reportForm.entity_ref = ctx.entity_ref;
|
|
413
|
+
this.reportForm.entity_label = ctx.title;
|
|
414
|
+
this.reportForm.title = ctx.title || `Report — ${ctx.entity_ref}`;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
highlightRef: {
|
|
419
|
+
immediate: true,
|
|
420
|
+
handler(ref) {
|
|
421
|
+
if (ref) {
|
|
422
|
+
this.highlightedRef = ref;
|
|
423
|
+
this.$nextTick(() => this.scrollToReport(ref));
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
},
|
|
139
428
|
|
|
140
429
|
methods: {
|
|
141
|
-
|
|
142
|
-
|
|
430
|
+
// ── Banner ──────────────────────────────────────────────
|
|
431
|
+
dismissBanner() { this.bannerDismissed = true; },
|
|
432
|
+
|
|
433
|
+
openReportForm() {
|
|
434
|
+
if (this.pendingContext && !this.reportForm.entity_ref) {
|
|
435
|
+
this.reportForm.entity_type = this.pendingContext.entity_type;
|
|
436
|
+
this.reportForm.entity_id = this.pendingContext.entity_id;
|
|
437
|
+
this.reportForm.entity_ref = this.pendingContext.entity_ref;
|
|
438
|
+
this.reportForm.entity_label = this.pendingContext.title;
|
|
439
|
+
this.reportForm.title = this.pendingContext.title || `Report — ${this.pendingContext.entity_ref}`;
|
|
440
|
+
}
|
|
441
|
+
this.showReportModal = true;
|
|
442
|
+
},
|
|
443
|
+
|
|
444
|
+
closeReportForm() {
|
|
445
|
+
this.showReportModal = false;
|
|
446
|
+
},
|
|
447
|
+
|
|
448
|
+
clearEntityLink() {
|
|
449
|
+
this.reportForm.entity_type = '';
|
|
450
|
+
this.reportForm.entity_id = '';
|
|
451
|
+
this.reportForm.entity_ref = '';
|
|
452
|
+
this.reportForm.entity_label = '';
|
|
453
|
+
this.reportForm.selectedMissing = '';
|
|
454
|
+
},
|
|
143
455
|
|
|
144
|
-
|
|
456
|
+
applyMissingSelection() {
|
|
457
|
+
if (!this.reportForm.selectedMissing) return;
|
|
458
|
+
const r = JSON.parse(this.reportForm.selectedMissing);
|
|
459
|
+
this.reportForm.entity_type = r.entity_type;
|
|
460
|
+
this.reportForm.entity_id = r.entity_id;
|
|
461
|
+
this.reportForm.entity_ref = r.entity_ref;
|
|
462
|
+
this.reportForm.entity_label = r.label;
|
|
463
|
+
this.reportForm.title = this.reportForm.title || `Report — ${r.entity_ref}`;
|
|
464
|
+
},
|
|
465
|
+
|
|
466
|
+
// ── Submit Report ────────────────────────────────────────
|
|
467
|
+
submitReport() {
|
|
468
|
+
const folder = this.folders.find(f => f.id === this.reportForm.folderId);
|
|
469
|
+
const report = {
|
|
145
470
|
id: Date.now(),
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
471
|
+
title: this.reportForm.title,
|
|
472
|
+
submittedBy: this.reportForm.submittedBy,
|
|
473
|
+
date: this.reportForm.date,
|
|
474
|
+
findings: this.reportForm.findings,
|
|
475
|
+
correctiveActions: this.reportForm.correctiveActions,
|
|
476
|
+
files: [...this.reportForm.files],
|
|
477
|
+
entity_type: this.reportForm.entity_type,
|
|
478
|
+
entity_id: this.reportForm.entity_id,
|
|
479
|
+
entity_ref: this.reportForm.entity_ref,
|
|
480
|
+
folderId: this.reportForm.folderId || null,
|
|
481
|
+
folderName: folder?.name || null,
|
|
482
|
+
submittedAt: new Date(),
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
this.linkedReports.push(report);
|
|
486
|
+
this.$emit('report-submitted', report);
|
|
487
|
+
this.$emit('data-updated', {
|
|
488
|
+
folders: this.folders,
|
|
489
|
+
allFiles: this.getAllFilesData(),
|
|
490
|
+
linkedReports: this.linkedReports,
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
// Reset form
|
|
494
|
+
this.reportForm = {
|
|
495
|
+
title: '', submittedBy: '', folderId: '',
|
|
496
|
+
date: new Date().toISOString().split('T')[0],
|
|
497
|
+
findings: '', correctiveActions: '', files: [],
|
|
498
|
+
entity_type: '', entity_id: '', entity_ref: '',
|
|
499
|
+
entity_label: '', selectedMissing: '',
|
|
149
500
|
};
|
|
501
|
+
this.showReportModal = false;
|
|
150
502
|
|
|
503
|
+
// Highlight the new report
|
|
504
|
+
this.$nextTick(() => {
|
|
505
|
+
this.highlightedRef = report.entity_ref;
|
|
506
|
+
this.scrollToReport(report.entity_ref);
|
|
507
|
+
setTimeout(() => { this.highlightedRef = null; }, 3000);
|
|
508
|
+
});
|
|
509
|
+
},
|
|
510
|
+
|
|
511
|
+
// ── View Report ──────────────────────────────────────────
|
|
512
|
+
viewReport(report) {
|
|
513
|
+
this.viewingReport = report;
|
|
514
|
+
this.showViewModal = true;
|
|
515
|
+
this.$emit('report-viewed', report);
|
|
516
|
+
},
|
|
517
|
+
|
|
518
|
+
scrollToReport(ref) {
|
|
519
|
+
this.$nextTick(() => {
|
|
520
|
+
const el = this.$refs['report-' + ref];
|
|
521
|
+
if (el) {
|
|
522
|
+
const target = Array.isArray(el) ? el[0] : el;
|
|
523
|
+
target?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
},
|
|
527
|
+
|
|
528
|
+
// ── File Helpers ─────────────────────────────────────────
|
|
529
|
+
handleReportFileAttach(e) {
|
|
530
|
+
this.reportForm.files.push(...Array.from(e.target.files));
|
|
531
|
+
e.target.value = '';
|
|
532
|
+
},
|
|
533
|
+
handleReportFileDrop(e) {
|
|
534
|
+
this.reportForm.files.push(...Array.from(e.dataTransfer.files));
|
|
535
|
+
},
|
|
536
|
+
removeReportFile(i) { this.reportForm.files.splice(i, 1); },
|
|
537
|
+
|
|
538
|
+
// ── Folder Methods ───────────────────────────────────────
|
|
539
|
+
createFolder() {
|
|
540
|
+
if (!this.newFolderName.trim()) return;
|
|
541
|
+
const newFolder = { id: Date.now(), name: this.newFolderName.trim(), files: [], createdAt: new Date() };
|
|
151
542
|
this.folders.push(newFolder);
|
|
152
543
|
this.$emit('folder-created', newFolder);
|
|
153
|
-
this.$emit('data-updated', { folders: this.folders, allFiles: this.getAllFilesData() });
|
|
154
|
-
|
|
544
|
+
this.$emit('data-updated', { folders: this.folders, allFiles: this.getAllFilesData(), linkedReports: this.linkedReports });
|
|
155
545
|
this.newFolderName = '';
|
|
156
546
|
this.showNewFolderModal = false;
|
|
157
547
|
},
|
|
158
548
|
|
|
159
|
-
selectFolder(folder) {
|
|
160
|
-
this.selectedFolder = folder;
|
|
161
|
-
this.$refs.fileInput.click();
|
|
162
|
-
},
|
|
549
|
+
selectFolder(folder) { this.selectedFolder = folder; this.$refs.fileInput.click(); },
|
|
163
550
|
|
|
164
551
|
handleFileSelection(event) {
|
|
165
552
|
const files = Array.from(event.target.files);
|
|
166
|
-
|
|
167
553
|
if (!this.selectedFolder || files.length === 0) return;
|
|
168
|
-
|
|
169
|
-
const newFiles = files.map(file => ({
|
|
170
|
-
id: Date.now() + Math.random(),
|
|
171
|
-
name: file.name,
|
|
172
|
-
size: file.size,
|
|
173
|
-
type: file.type,
|
|
174
|
-
file: file,
|
|
175
|
-
addedAt: new Date()
|
|
176
|
-
}));
|
|
177
|
-
|
|
554
|
+
const newFiles = files.map(file => ({ id: Date.now() + Math.random(), name: file.name, size: file.size, type: file.type, file, addedAt: new Date() }));
|
|
178
555
|
this.selectedFolder.files.push(...newFiles);
|
|
179
|
-
|
|
180
|
-
this.$emit('
|
|
181
|
-
folderId: this.selectedFolder.id,
|
|
182
|
-
files: newFiles
|
|
183
|
-
});
|
|
184
|
-
this.$emit('data-updated', { folders: this.folders, allFiles: this.getAllFilesData() });
|
|
185
|
-
|
|
186
|
-
// Reset file input
|
|
556
|
+
this.$emit('files-added', { folderId: this.selectedFolder.id, files: newFiles });
|
|
557
|
+
this.$emit('data-updated', { folders: this.folders, allFiles: this.getAllFilesData(), linkedReports: this.linkedReports });
|
|
187
558
|
event.target.value = '';
|
|
188
559
|
this.selectedFolder = null;
|
|
189
560
|
},
|
|
@@ -191,226 +562,324 @@ export default {
|
|
|
191
562
|
removeFile(folderId, fileId) {
|
|
192
563
|
const folder = this.folders.find(f => f.id === folderId);
|
|
193
564
|
if (folder) {
|
|
194
|
-
const
|
|
195
|
-
if (
|
|
196
|
-
const removedFile = folder.files.splice(
|
|
197
|
-
this.$emit('file-removed', {
|
|
198
|
-
|
|
199
|
-
file: removedFile
|
|
200
|
-
});
|
|
201
|
-
this.$emit('data-updated', { folders: this.folders, allFiles: this.getAllFilesData() });
|
|
565
|
+
const i = folder.files.findIndex(f => f.id === fileId);
|
|
566
|
+
if (i > -1) {
|
|
567
|
+
const removedFile = folder.files.splice(i, 1)[0];
|
|
568
|
+
this.$emit('file-removed', { folderId, file: removedFile });
|
|
569
|
+
this.$emit('data-updated', { folders: this.folders, allFiles: this.getAllFilesData(), linkedReports: this.linkedReports });
|
|
202
570
|
}
|
|
203
571
|
}
|
|
204
572
|
},
|
|
205
573
|
|
|
206
574
|
deleteFolder(folderId) {
|
|
207
|
-
if (confirm('
|
|
208
|
-
const
|
|
209
|
-
if (
|
|
210
|
-
const
|
|
211
|
-
this.$emit('folder-deleted',
|
|
212
|
-
this.$emit('data-updated', { folders: this.folders, allFiles: this.getAllFilesData() });
|
|
575
|
+
if (confirm('Delete this folder and all its files?')) {
|
|
576
|
+
const i = this.folders.findIndex(f => f.id === folderId);
|
|
577
|
+
if (i > -1) {
|
|
578
|
+
const deleted = this.folders.splice(i, 1)[0];
|
|
579
|
+
this.$emit('folder-deleted', deleted);
|
|
580
|
+
this.$emit('data-updated', { folders: this.folders, allFiles: this.getAllFilesData(), linkedReports: this.linkedReports });
|
|
213
581
|
}
|
|
214
582
|
}
|
|
215
583
|
},
|
|
216
584
|
|
|
585
|
+
toggleFolder(folderId) {
|
|
586
|
+
const i = this.expandedFolders.indexOf(folderId);
|
|
587
|
+
this.expandedFolders = i > -1 ? [] : [folderId];
|
|
588
|
+
},
|
|
589
|
+
closeAllFolders() { this.expandedFolders = []; },
|
|
590
|
+
getExpandedFolder() {
|
|
591
|
+
return this.expandedFolders.length > 0
|
|
592
|
+
? this.folders.find(f => f.id === this.expandedFolders[0]) || { files: [], name: '' }
|
|
593
|
+
: { files: [], name: '' };
|
|
594
|
+
},
|
|
595
|
+
showPreview(id) { this.previewFolder = id; },
|
|
596
|
+
hidePreview() { this.previewFolder = null; },
|
|
597
|
+
|
|
217
598
|
formatFileSize(bytes) {
|
|
218
|
-
if (bytes
|
|
219
|
-
const k = 1024;
|
|
220
|
-
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
599
|
+
if (!bytes) return '0 Bytes';
|
|
600
|
+
const k = 1024, sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
221
601
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
222
602
|
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
|
223
603
|
},
|
|
224
|
-
|
|
604
|
+
formatDate(d) { return d ? new Date(d).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' }) : '—'; },
|
|
225
605
|
getAllFilesData() {
|
|
226
|
-
return this.folders.flatMap(folder =>
|
|
227
|
-
folder.files.map(file => ({
|
|
228
|
-
...file,
|
|
229
|
-
folderName: folder.name,
|
|
230
|
-
folderId: folder.id
|
|
231
|
-
}))
|
|
606
|
+
return this.folders.flatMap(folder =>
|
|
607
|
+
folder.files.map(file => ({ ...file, folderName: folder.name, folderId: folder.id }))
|
|
232
608
|
);
|
|
233
609
|
},
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
</script>
|
|
234
613
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
} else {
|
|
240
|
-
this.expandedFolders = [folderId]; // Only one folder expanded at a time
|
|
241
|
-
}
|
|
242
|
-
},
|
|
614
|
+
<style scoped>
|
|
615
|
+
.reports-container {
|
|
616
|
+
padding: 24px;
|
|
617
|
+
}
|
|
243
618
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
619
|
+
/* ── Banner ── */
|
|
620
|
+
.missing-banner {
|
|
621
|
+
background: linear-gradient(135deg, #fff3cd, #ffe8a1);
|
|
622
|
+
border: 1.5px solid #f0ad4e;
|
|
623
|
+
border-radius: 10px;
|
|
624
|
+
margin-bottom: 22px;
|
|
625
|
+
overflow: hidden;
|
|
626
|
+
}
|
|
247
627
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
628
|
+
.missing-banner-inner {
|
|
629
|
+
display: flex;
|
|
630
|
+
align-items: center;
|
|
631
|
+
gap: 14px;
|
|
632
|
+
padding: 14px 18px;
|
|
633
|
+
flex-wrap: wrap;
|
|
634
|
+
}
|
|
254
635
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
636
|
+
.missing-icon {
|
|
637
|
+
font-size: 22px;
|
|
638
|
+
color: #856404;
|
|
639
|
+
flex-shrink: 0;
|
|
640
|
+
}
|
|
258
641
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
642
|
+
.missing-content {
|
|
643
|
+
flex: 1;
|
|
644
|
+
min-width: 0;
|
|
263
645
|
}
|
|
264
|
-
</script>
|
|
265
646
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
647
|
+
.missing-content strong {
|
|
648
|
+
font-size: 14px;
|
|
649
|
+
color: #664d03;
|
|
650
|
+
display: block;
|
|
269
651
|
}
|
|
270
652
|
|
|
653
|
+
.missing-content p {
|
|
654
|
+
margin: 2px 0 0;
|
|
655
|
+
font-size: 12px;
|
|
656
|
+
color: #856404;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.ref-chip {
|
|
660
|
+
background: #856404;
|
|
661
|
+
color: #fff;
|
|
662
|
+
border-radius: 4px;
|
|
663
|
+
padding: 1px 8px;
|
|
664
|
+
font-family: monospace;
|
|
665
|
+
font-size: 12px;
|
|
666
|
+
font-weight: 700;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
.btn-dismiss {
|
|
670
|
+
background: none;
|
|
671
|
+
border: none;
|
|
672
|
+
color: #856404;
|
|
673
|
+
cursor: pointer;
|
|
674
|
+
font-size: 15px;
|
|
675
|
+
padding: 4px 6px;
|
|
676
|
+
border-radius: 4px;
|
|
677
|
+
transition: background .15s;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.btn-dismiss:hover {
|
|
681
|
+
background: rgba(0, 0, 0, .08);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.slide-down-enter-active,
|
|
685
|
+
.slide-down-leave-active {
|
|
686
|
+
transition: all .25s;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.slide-down-enter-from,
|
|
690
|
+
.slide-down-leave-to {
|
|
691
|
+
opacity: 0;
|
|
692
|
+
transform: translateY(-12px);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/* ── Page Header ── */
|
|
271
696
|
.page-header {
|
|
272
|
-
margin-bottom:
|
|
697
|
+
margin-bottom: 24px;
|
|
273
698
|
border-bottom: 1px solid #e0e0e0;
|
|
274
|
-
padding-bottom:
|
|
699
|
+
padding-bottom: 16px;
|
|
275
700
|
}
|
|
276
701
|
|
|
277
|
-
.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
702
|
+
.page-title {
|
|
703
|
+
font-size: 22px;
|
|
704
|
+
font-weight: 700;
|
|
705
|
+
color: #1e3c72;
|
|
706
|
+
margin: 0;
|
|
282
707
|
}
|
|
283
708
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
709
|
+
.page-subtitle {
|
|
710
|
+
font-size: 13px;
|
|
711
|
+
color: #6c757d;
|
|
712
|
+
margin: 2px 0 0;
|
|
289
713
|
}
|
|
290
714
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
715
|
+
.header-actions {
|
|
716
|
+
display: flex;
|
|
717
|
+
gap: 8px;
|
|
295
718
|
}
|
|
296
719
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
720
|
+
/* ── Section Label ── */
|
|
721
|
+
.section-label {
|
|
722
|
+
font-size: 12px;
|
|
723
|
+
font-weight: 700;
|
|
724
|
+
text-transform: uppercase;
|
|
725
|
+
letter-spacing: .06em;
|
|
726
|
+
color: #495057;
|
|
727
|
+
margin-bottom: 14px;
|
|
728
|
+
display: flex;
|
|
729
|
+
align-items: center;
|
|
730
|
+
gap: 7px;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/* ── Report Cards ── */
|
|
734
|
+
.report-cards {
|
|
306
735
|
display: flex;
|
|
307
736
|
flex-direction: column;
|
|
737
|
+
gap: 10px;
|
|
738
|
+
margin-bottom: 24px;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
.report-card {
|
|
742
|
+
display: flex;
|
|
308
743
|
align-items: center;
|
|
309
|
-
|
|
744
|
+
gap: 14px;
|
|
745
|
+
background: #fff;
|
|
746
|
+
border: 1.5px solid #e9ecef;
|
|
747
|
+
border-radius: 10px;
|
|
748
|
+
padding: 14px 18px;
|
|
749
|
+
cursor: pointer;
|
|
750
|
+
transition: all .2s;
|
|
310
751
|
}
|
|
311
752
|
|
|
312
|
-
.
|
|
313
|
-
border-color: #
|
|
314
|
-
box-shadow: 0
|
|
315
|
-
transform: translateY(-2px);
|
|
753
|
+
.report-card:hover {
|
|
754
|
+
border-color: #2a5298;
|
|
755
|
+
box-shadow: 0 2px 10px rgba(42, 82, 152, .1);
|
|
316
756
|
}
|
|
317
757
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
transform: translate(-50%, -50%);
|
|
324
|
-
background: white;
|
|
325
|
-
border: 2px solid #0d6efd;
|
|
326
|
-
border-radius: 8px;
|
|
327
|
-
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
|
|
328
|
-
padding: 0;
|
|
329
|
-
z-index: 100;
|
|
330
|
-
min-width: 280px;
|
|
331
|
-
max-width: 320px;
|
|
332
|
-
pointer-events: none;
|
|
758
|
+
.report-card--highlighted {
|
|
759
|
+
border-color: #f0ad4e !important;
|
|
760
|
+
background: #fffbf0 !important;
|
|
761
|
+
box-shadow: 0 0 0 3px rgba(240, 173, 78, .25) !important;
|
|
762
|
+
animation: flash-highlight 1.5s ease;
|
|
333
763
|
}
|
|
334
764
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
765
|
+
@keyframes flash-highlight {
|
|
766
|
+
|
|
767
|
+
0%,
|
|
768
|
+
100% {
|
|
769
|
+
background: #fffbf0;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
50% {
|
|
773
|
+
background: #fff3cd;
|
|
774
|
+
}
|
|
340
775
|
}
|
|
341
776
|
|
|
342
|
-
.
|
|
343
|
-
font-size:
|
|
344
|
-
|
|
777
|
+
.rc-badge {
|
|
778
|
+
font-size: 10px;
|
|
779
|
+
font-weight: 700;
|
|
780
|
+
padding: 3px 8px;
|
|
781
|
+
border-radius: 4px;
|
|
782
|
+
text-transform: uppercase;
|
|
783
|
+
white-space: nowrap;
|
|
784
|
+
flex-shrink: 0;
|
|
345
785
|
}
|
|
346
786
|
|
|
347
|
-
.
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
overflow-y: auto;
|
|
787
|
+
.rc-badge.drill {
|
|
788
|
+
background: #d1ecf1;
|
|
789
|
+
color: #0c5460;
|
|
351
790
|
}
|
|
352
791
|
|
|
353
|
-
.
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
gap: 8px;
|
|
357
|
-
padding: 6px 8px;
|
|
358
|
-
font-size: 0.85rem;
|
|
359
|
-
color: #495057;
|
|
360
|
-
border-bottom: 1px solid #f0f0f0;
|
|
792
|
+
.rc-badge.incident {
|
|
793
|
+
background: #f8d7da;
|
|
794
|
+
color: #721c24;
|
|
361
795
|
}
|
|
362
796
|
|
|
363
|
-
.
|
|
364
|
-
|
|
797
|
+
.rc-body {
|
|
798
|
+
display: flex;
|
|
799
|
+
flex-direction: column;
|
|
800
|
+
flex: 1;
|
|
801
|
+
min-width: 0;
|
|
365
802
|
}
|
|
366
803
|
|
|
367
|
-
.
|
|
368
|
-
|
|
369
|
-
font-size:
|
|
804
|
+
.rc-ref {
|
|
805
|
+
font-family: monospace;
|
|
806
|
+
font-size: 13px;
|
|
807
|
+
color: #2a5298;
|
|
808
|
+
font-weight: 700;
|
|
370
809
|
}
|
|
371
810
|
|
|
372
|
-
.
|
|
811
|
+
.rc-title {
|
|
812
|
+
font-size: 14px;
|
|
813
|
+
color: #212529;
|
|
373
814
|
white-space: nowrap;
|
|
374
815
|
overflow: hidden;
|
|
375
816
|
text-overflow: ellipsis;
|
|
376
817
|
}
|
|
377
818
|
|
|
378
|
-
.
|
|
379
|
-
|
|
380
|
-
padding: 8px;
|
|
381
|
-
font-size: 0.8rem;
|
|
819
|
+
.rc-meta {
|
|
820
|
+
font-size: 11px;
|
|
382
821
|
color: #6c757d;
|
|
383
|
-
|
|
822
|
+
margin-top: 2px;
|
|
384
823
|
}
|
|
385
824
|
|
|
386
|
-
.
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
825
|
+
.rc-folder {
|
|
826
|
+
font-size: 11px;
|
|
827
|
+
color: #6c757d;
|
|
828
|
+
background: #f8f9fa;
|
|
829
|
+
border-radius: 4px;
|
|
830
|
+
padding: 2px 8px;
|
|
831
|
+
flex-shrink: 0;
|
|
390
832
|
}
|
|
391
833
|
|
|
392
|
-
.
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
margin-bottom: 8px;
|
|
834
|
+
.rc-arrow {
|
|
835
|
+
color: #ced4da;
|
|
836
|
+
font-size: 14px;
|
|
396
837
|
}
|
|
397
838
|
|
|
398
|
-
|
|
839
|
+
/* ── Folders ── */
|
|
840
|
+
.folders-section {
|
|
841
|
+
display: grid;
|
|
842
|
+
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
|
843
|
+
gap: 18px;
|
|
844
|
+
padding: 4px 0;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
.folder-card {
|
|
848
|
+
position: relative;
|
|
849
|
+
border: 2px solid #e0e0e0;
|
|
850
|
+
border-radius: 10px;
|
|
851
|
+
padding: 15px;
|
|
852
|
+
background: #fff;
|
|
853
|
+
cursor: pointer;
|
|
854
|
+
aspect-ratio: 1;
|
|
399
855
|
display: flex;
|
|
400
856
|
flex-direction: column;
|
|
401
857
|
align-items: center;
|
|
402
858
|
justify-content: center;
|
|
859
|
+
transition: all .2s;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
.folder-card:hover {
|
|
863
|
+
border-color: #2a5298;
|
|
864
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, .1);
|
|
865
|
+
transform: translateY(-2px);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
.folder-content {
|
|
869
|
+
display: flex;
|
|
870
|
+
flex-direction: column;
|
|
871
|
+
align-items: center;
|
|
403
872
|
text-align: center;
|
|
404
873
|
width: 100%;
|
|
405
874
|
height: 100%;
|
|
875
|
+
justify-content: center;
|
|
406
876
|
}
|
|
407
877
|
|
|
408
878
|
.folder-name {
|
|
409
|
-
margin-top:
|
|
879
|
+
margin-top: 8px;
|
|
410
880
|
font-weight: 600;
|
|
411
|
-
font-size:
|
|
881
|
+
font-size: .85rem;
|
|
412
882
|
word-break: break-word;
|
|
413
|
-
max-width: 100%;
|
|
414
883
|
overflow: hidden;
|
|
415
884
|
text-overflow: ellipsis;
|
|
416
885
|
display: -webkit-box;
|
|
@@ -423,9 +892,9 @@ export default {
|
|
|
423
892
|
top: 5px;
|
|
424
893
|
right: 5px;
|
|
425
894
|
display: flex;
|
|
426
|
-
gap:
|
|
895
|
+
gap: 4px;
|
|
427
896
|
opacity: 0;
|
|
428
|
-
transition: opacity
|
|
897
|
+
transition: opacity .2s;
|
|
429
898
|
}
|
|
430
899
|
|
|
431
900
|
.folder-card:hover .folder-actions-overlay {
|
|
@@ -439,28 +908,31 @@ export default {
|
|
|
439
908
|
display: flex;
|
|
440
909
|
align-items: center;
|
|
441
910
|
justify-content: center;
|
|
442
|
-
background:
|
|
911
|
+
background: #fff;
|
|
443
912
|
border: 1px solid #dee2e6;
|
|
444
913
|
border-radius: 4px;
|
|
914
|
+
cursor: pointer;
|
|
445
915
|
}
|
|
446
916
|
|
|
447
917
|
.btn-icon:hover {
|
|
448
918
|
background: #f8f9fa;
|
|
449
|
-
border-color: #
|
|
919
|
+
border-color: #2a5298;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
.btn-icon-danger:hover {
|
|
923
|
+
border-color: #dc3545;
|
|
924
|
+
color: #dc3545;
|
|
450
925
|
}
|
|
451
926
|
|
|
452
927
|
.btn-icon i {
|
|
453
|
-
font-size:
|
|
928
|
+
font-size: .82rem;
|
|
454
929
|
}
|
|
455
930
|
|
|
456
|
-
/* Expanded Folder
|
|
931
|
+
/* ── Expanded Folder ── */
|
|
457
932
|
.folder-modal-overlay {
|
|
458
933
|
position: fixed;
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
right: 0;
|
|
462
|
-
bottom: 0;
|
|
463
|
-
background: rgba(0,0,0,0.5);
|
|
934
|
+
inset: 0;
|
|
935
|
+
background: rgba(0, 0, 0, .5);
|
|
464
936
|
z-index: 1000;
|
|
465
937
|
display: flex;
|
|
466
938
|
align-items: center;
|
|
@@ -468,41 +940,39 @@ export default {
|
|
|
468
940
|
}
|
|
469
941
|
|
|
470
942
|
.folder-expanded {
|
|
471
|
-
background:
|
|
943
|
+
background: #fff;
|
|
472
944
|
border-radius: 12px;
|
|
473
|
-
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
|
474
945
|
width: 90%;
|
|
475
946
|
max-width: 700px;
|
|
476
947
|
max-height: 80vh;
|
|
477
948
|
overflow: hidden;
|
|
478
949
|
display: flex;
|
|
479
950
|
flex-direction: column;
|
|
951
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, .3);
|
|
480
952
|
}
|
|
481
953
|
|
|
482
954
|
.expanded-header {
|
|
483
955
|
display: flex;
|
|
484
956
|
justify-content: space-between;
|
|
485
957
|
align-items: center;
|
|
486
|
-
padding:
|
|
958
|
+
padding: 18px 24px;
|
|
487
959
|
border-bottom: 2px solid #f0f0f0;
|
|
488
960
|
background: #f8f9fa;
|
|
489
961
|
}
|
|
490
962
|
|
|
491
963
|
.expanded-header strong {
|
|
492
|
-
font-size: 1.
|
|
493
|
-
color: #333;
|
|
964
|
+
font-size: 1.1rem;
|
|
494
965
|
}
|
|
495
966
|
|
|
496
967
|
.btn-close-expanded {
|
|
497
968
|
background: transparent;
|
|
498
969
|
border: none;
|
|
499
|
-
font-size: 1.
|
|
970
|
+
font-size: 1.1rem;
|
|
500
971
|
cursor: pointer;
|
|
501
|
-
padding: 5px
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
transition: color 0.2s;
|
|
972
|
+
padding: 5px 8px;
|
|
973
|
+
color: #666;
|
|
974
|
+
border-radius: 4px;
|
|
975
|
+
transition: color .2s;
|
|
506
976
|
}
|
|
507
977
|
|
|
508
978
|
.btn-close-expanded:hover {
|
|
@@ -510,22 +980,18 @@ export default {
|
|
|
510
980
|
}
|
|
511
981
|
|
|
512
982
|
.files-list {
|
|
513
|
-
padding:
|
|
514
|
-
max-height:
|
|
983
|
+
padding: 18px;
|
|
984
|
+
max-height: 55vh;
|
|
515
985
|
overflow-y: auto;
|
|
516
986
|
}
|
|
517
987
|
|
|
518
988
|
.file-item {
|
|
519
|
-
padding:
|
|
520
|
-
background
|
|
521
|
-
border-radius:
|
|
989
|
+
padding: 10px 14px;
|
|
990
|
+
background: #f8f9fa;
|
|
991
|
+
border-radius: 6px;
|
|
522
992
|
margin-bottom: 8px;
|
|
523
993
|
}
|
|
524
994
|
|
|
525
|
-
.file-item:last-child {
|
|
526
|
-
margin-bottom: 0;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
995
|
.empty-folder-message {
|
|
530
996
|
padding: 40px 20px;
|
|
531
997
|
text-align: center;
|
|
@@ -533,29 +999,21 @@ export default {
|
|
|
533
999
|
}
|
|
534
1000
|
|
|
535
1001
|
.empty-folder-message i {
|
|
536
|
-
font-size:
|
|
1002
|
+
font-size: 2.5rem;
|
|
537
1003
|
color: #dee2e6;
|
|
538
1004
|
}
|
|
539
1005
|
|
|
540
1006
|
.empty-folder-message p {
|
|
541
|
-
margin:
|
|
1007
|
+
margin: 12px 0;
|
|
542
1008
|
}
|
|
543
1009
|
|
|
544
|
-
|
|
545
|
-
padding: 60px 20px;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
/* Modal Styles */
|
|
1010
|
+
/* ── Modals ── */
|
|
549
1011
|
.modal {
|
|
550
1012
|
display: none;
|
|
551
1013
|
position: fixed;
|
|
552
1014
|
z-index: 1050;
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
width: 100%;
|
|
556
|
-
height: 100%;
|
|
557
|
-
overflow: hidden;
|
|
558
|
-
background-color: rgba(0,0,0,0.5);
|
|
1015
|
+
inset: 0;
|
|
1016
|
+
background: rgba(0, 0, 0, .5);
|
|
559
1017
|
align-items: center;
|
|
560
1018
|
justify-content: center;
|
|
561
1019
|
}
|
|
@@ -566,17 +1024,23 @@ export default {
|
|
|
566
1024
|
|
|
567
1025
|
.modal-dialog {
|
|
568
1026
|
max-width: 500px;
|
|
569
|
-
|
|
1027
|
+
width: 95%;
|
|
1028
|
+
margin: 1.5rem auto;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
.modal-dialog.modal-lg {
|
|
1032
|
+
max-width: 780px;
|
|
570
1033
|
}
|
|
571
1034
|
|
|
572
1035
|
.modal-content {
|
|
573
|
-
background
|
|
574
|
-
border-radius:
|
|
575
|
-
box-shadow: 0
|
|
1036
|
+
background: #fff;
|
|
1037
|
+
border-radius: 12px;
|
|
1038
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, .25);
|
|
1039
|
+
overflow: hidden;
|
|
576
1040
|
}
|
|
577
1041
|
|
|
578
1042
|
.modal-header {
|
|
579
|
-
padding:
|
|
1043
|
+
padding: 18px 22px;
|
|
580
1044
|
border-bottom: 1px solid #dee2e6;
|
|
581
1045
|
display: flex;
|
|
582
1046
|
justify-content: space-between;
|
|
@@ -584,29 +1048,402 @@ export default {
|
|
|
584
1048
|
}
|
|
585
1049
|
|
|
586
1050
|
.modal-body {
|
|
587
|
-
padding:
|
|
1051
|
+
padding: 22px;
|
|
1052
|
+
max-height: 70vh;
|
|
1053
|
+
overflow-y: auto;
|
|
588
1054
|
}
|
|
589
1055
|
|
|
590
1056
|
.modal-footer {
|
|
591
|
-
padding:
|
|
1057
|
+
padding: 14px 22px;
|
|
592
1058
|
border-top: 1px solid #dee2e6;
|
|
593
1059
|
display: flex;
|
|
594
1060
|
justify-content: flex-end;
|
|
595
1061
|
gap: 10px;
|
|
596
1062
|
}
|
|
597
1063
|
|
|
1064
|
+
.rpt-modal-header {
|
|
1065
|
+
background: linear-gradient(135deg, #1e3c72, #2a5298);
|
|
1066
|
+
color: #fff;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
598
1069
|
.btn-close {
|
|
599
1070
|
background: transparent;
|
|
600
1071
|
border: none;
|
|
601
|
-
font-size: 1.
|
|
602
|
-
font-weight: 700;
|
|
603
|
-
line-height: 1;
|
|
604
|
-
color: #000;
|
|
605
|
-
opacity: 0.5;
|
|
1072
|
+
font-size: 1.4rem;
|
|
606
1073
|
cursor: pointer;
|
|
1074
|
+
opacity: .6;
|
|
607
1075
|
}
|
|
608
1076
|
|
|
609
1077
|
.btn-close:hover {
|
|
610
|
-
opacity:
|
|
1078
|
+
opacity: 1;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/* ── Report Form ── */
|
|
1082
|
+
.rpt-entity-box {
|
|
1083
|
+
background: #f8f9fa;
|
|
1084
|
+
border: 1px solid #e0e0e0;
|
|
1085
|
+
border-radius: 8px;
|
|
1086
|
+
padding: 14px 16px;
|
|
1087
|
+
margin-bottom: 4px;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
.prefilled-entity {
|
|
1091
|
+
display: flex;
|
|
1092
|
+
align-items: center;
|
|
1093
|
+
gap: 10px;
|
|
1094
|
+
flex-wrap: wrap;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
.entity-chip {
|
|
1098
|
+
font-size: 10px;
|
|
1099
|
+
font-weight: 700;
|
|
1100
|
+
padding: 2px 8px;
|
|
1101
|
+
border-radius: 4px;
|
|
1102
|
+
text-transform: uppercase;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
.entity-chip.drill {
|
|
1106
|
+
background: #d1ecf1;
|
|
1107
|
+
color: #0c5460;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
.entity-chip.incident {
|
|
1111
|
+
background: #f8d7da;
|
|
1112
|
+
color: #721c24;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
.input-row {
|
|
1116
|
+
display: grid;
|
|
1117
|
+
grid-template-columns: 1fr 1fr;
|
|
1118
|
+
gap: 14px;
|
|
1119
|
+
margin-bottom: 14px;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
.form-group {
|
|
1123
|
+
display: flex;
|
|
1124
|
+
flex-direction: column;
|
|
1125
|
+
margin-bottom: 14px;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
.form-label {
|
|
1129
|
+
font-size: 13px;
|
|
1130
|
+
margin-bottom: 5px;
|
|
1131
|
+
color: #495057;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
.form-control {
|
|
1135
|
+
padding: 9px 12px;
|
|
1136
|
+
border: 1px solid #ced4da;
|
|
1137
|
+
border-radius: 6px;
|
|
1138
|
+
font-size: 14px;
|
|
1139
|
+
transition: border-color .2s;
|
|
1140
|
+
width: 100%;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
.form-control:focus {
|
|
1144
|
+
outline: none;
|
|
1145
|
+
border-color: #2a5298;
|
|
1146
|
+
box-shadow: 0 0 0 3px rgba(42, 82, 152, .1);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
textarea.form-control {
|
|
1150
|
+
resize: vertical;
|
|
1151
|
+
min-height: 80px;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
select.form-control {
|
|
1155
|
+
cursor: pointer;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/* File drop zone */
|
|
1159
|
+
.file-drop-zone {
|
|
1160
|
+
border: 2px dashed #ced4da;
|
|
1161
|
+
border-radius: 8px;
|
|
1162
|
+
padding: 20px;
|
|
1163
|
+
text-align: center;
|
|
1164
|
+
cursor: pointer;
|
|
1165
|
+
transition: border-color .2s;
|
|
1166
|
+
min-height: 80px;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
.file-drop-zone:hover {
|
|
1170
|
+
border-color: #2a5298;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
.attached-files {
|
|
1174
|
+
display: flex;
|
|
1175
|
+
flex-wrap: wrap;
|
|
1176
|
+
gap: 6px;
|
|
1177
|
+
margin-top: 10px;
|
|
1178
|
+
justify-content: center;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
.attached-chip {
|
|
1182
|
+
background: #e9ecef;
|
|
1183
|
+
border-radius: 5px;
|
|
1184
|
+
padding: 3px 10px;
|
|
1185
|
+
font-size: 12px;
|
|
1186
|
+
display: flex;
|
|
1187
|
+
align-items: center;
|
|
1188
|
+
gap: 5px;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
.attached-chip button {
|
|
1192
|
+
background: none;
|
|
1193
|
+
border: none;
|
|
1194
|
+
cursor: pointer;
|
|
1195
|
+
color: #dc3545;
|
|
1196
|
+
padding: 0;
|
|
1197
|
+
font-size: 11px;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/* ── Report Viewer ── */
|
|
1201
|
+
.view-grid {
|
|
1202
|
+
display: grid;
|
|
1203
|
+
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
|
1204
|
+
gap: 14px;
|
|
1205
|
+
margin-bottom: 20px;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
.view-item {
|
|
1209
|
+
display: flex;
|
|
1210
|
+
flex-direction: column;
|
|
1211
|
+
gap: 3px;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
.view-item label {
|
|
1215
|
+
font-size: 11px;
|
|
1216
|
+
font-weight: 700;
|
|
1217
|
+
text-transform: uppercase;
|
|
1218
|
+
letter-spacing: .05em;
|
|
1219
|
+
color: #6c757d;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
.view-item span {
|
|
1223
|
+
font-size: 14px;
|
|
1224
|
+
color: #212529;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
.ref-mono {
|
|
1228
|
+
font-family: monospace;
|
|
1229
|
+
font-size: 13px;
|
|
1230
|
+
color: #2a5298;
|
|
1231
|
+
font-weight: 700;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
.view-section {
|
|
1235
|
+
margin-bottom: 18px;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
.view-section label {
|
|
1239
|
+
font-size: 12px;
|
|
1240
|
+
font-weight: 700;
|
|
1241
|
+
text-transform: uppercase;
|
|
1242
|
+
letter-spacing: .05em;
|
|
1243
|
+
color: #6c757d;
|
|
1244
|
+
display: block;
|
|
1245
|
+
margin-bottom: 6px;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
.view-text {
|
|
1249
|
+
background: #f8f9fa;
|
|
1250
|
+
border-radius: 6px;
|
|
1251
|
+
padding: 12px 14px;
|
|
1252
|
+
font-size: 14px;
|
|
1253
|
+
line-height: 1.6;
|
|
1254
|
+
color: #212529;
|
|
1255
|
+
white-space: pre-wrap;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
/* ── Buttons ── */
|
|
1259
|
+
.btn {
|
|
1260
|
+
padding: 9px 18px;
|
|
1261
|
+
border: none;
|
|
1262
|
+
border-radius: 8px;
|
|
1263
|
+
font-size: 13px;
|
|
1264
|
+
font-weight: 600;
|
|
1265
|
+
cursor: pointer;
|
|
1266
|
+
transition: all .2s;
|
|
1267
|
+
display: inline-flex;
|
|
1268
|
+
align-items: center;
|
|
1269
|
+
gap: 6px;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
.btn:disabled {
|
|
1273
|
+
opacity: .6;
|
|
1274
|
+
cursor: not-allowed;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
.btn-primary {
|
|
1278
|
+
background: #2a5298;
|
|
1279
|
+
color: #fff;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
.btn-primary:hover:not(:disabled) {
|
|
1283
|
+
background: #1e3c72;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
.btn-success {
|
|
1287
|
+
background: #28a745;
|
|
1288
|
+
color: #fff;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
.btn-success:hover:not(:disabled) {
|
|
1292
|
+
background: #218838;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
.btn-secondary {
|
|
1296
|
+
background: #6c757d;
|
|
1297
|
+
color: #fff;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
.btn-secondary:hover:not(:disabled) {
|
|
1301
|
+
background: #5a6268;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
.btn-outline-primary {
|
|
1305
|
+
background: transparent;
|
|
1306
|
+
border: 1.5px solid #2a5298;
|
|
1307
|
+
color: #2a5298;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
.btn-outline-primary:hover {
|
|
1311
|
+
background: #2a5298;
|
|
1312
|
+
color: #fff;
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
.btn-outline-danger {
|
|
1316
|
+
background: transparent;
|
|
1317
|
+
border: 1.5px solid #dc3545;
|
|
1318
|
+
color: #dc3545;
|
|
1319
|
+
padding: 4px 10px;
|
|
1320
|
+
font-size: 12px;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
.btn-outline-danger:hover {
|
|
1324
|
+
background: #dc3545;
|
|
1325
|
+
color: #fff;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
.btn-warning {
|
|
1329
|
+
background: #f0ad4e;
|
|
1330
|
+
color: #664d03;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
.btn-warning:hover {
|
|
1334
|
+
background: #e0972e;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
.btn-link {
|
|
1338
|
+
background: none;
|
|
1339
|
+
border: none;
|
|
1340
|
+
padding: 0;
|
|
1341
|
+
font-size: 13px;
|
|
1342
|
+
text-decoration: none;
|
|
1343
|
+
cursor: pointer;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
.btn-sm {
|
|
1347
|
+
padding: 5px 12px;
|
|
1348
|
+
font-size: 12px;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
.fw-bold {
|
|
1352
|
+
font-weight: 700;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
.fw-semibold {
|
|
1356
|
+
font-weight: 600;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
.me-1 {
|
|
1360
|
+
margin-right: 4px;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
.me-2 {
|
|
1364
|
+
margin-right: 8px;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
.ms-2 {
|
|
1368
|
+
margin-left: 8px;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
.ms-auto {
|
|
1372
|
+
margin-left: auto;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
.mt-3 {
|
|
1376
|
+
margin-top: 16px;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
.mt-4 {
|
|
1380
|
+
margin-top: 24px;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
.mt-1 {
|
|
1384
|
+
margin-top: 4px;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
.mb-0 {
|
|
1388
|
+
margin-bottom: 0;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
.mb-3 {
|
|
1392
|
+
margin-bottom: 16px;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
.text-muted {
|
|
1396
|
+
color: #6c757d !important;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
.text-danger {
|
|
1400
|
+
color: #dc3545 !important;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
.text-success {
|
|
1404
|
+
color: #28a745 !important;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
.text-warning {
|
|
1408
|
+
color: #f0ad4e !important;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
.d-flex {
|
|
1412
|
+
display: flex;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
.align-items-center {
|
|
1416
|
+
align-items: center;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
.justify-content-between {
|
|
1420
|
+
justify-content: space-between;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
.text-center {
|
|
1424
|
+
text-align: center;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
.empty-state {
|
|
1428
|
+
padding: 60px 20px;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
.empty-state h5 {
|
|
1432
|
+
margin-top: 14px;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
@media (max-width: 640px) {
|
|
1436
|
+
.input-row {
|
|
1437
|
+
grid-template-columns: 1fr;
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
.header-actions {
|
|
1441
|
+
gap: 6px;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
.btn {
|
|
1445
|
+
padding: 8px 12px;
|
|
1446
|
+
font-size: 12px;
|
|
1447
|
+
}
|
|
611
1448
|
}
|
|
612
1449
|
</style>
|