oceanhelm 0.0.1
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/LICENSE +35 -0
- package/README.md +121 -0
- package/dist/favico.ico +0 -0
- package/dist/oceanhelm.es.js +2134 -0
- package/dist/oceanhelm.es.js.map +1 -0
- package/dist/oceanhelm.umd.js +2 -0
- package/dist/oceanhelm.umd.js.map +1 -0
- package/dist/style.css +1 -0
- package/package.json +55 -0
- package/src/App.vue +10 -0
- package/src/assets/logo.svg +1 -0
- package/src/assets/main.css +0 -0
- package/src/components/ActivityLogs.vue +483 -0
- package/src/components/ConfigurableSidebar.vue +239 -0
- package/src/components/CrewManagement.vue +746 -0
- package/src/components/DashHead.vue +54 -0
- package/src/components/InventoryManagement.vue +1615 -0
- package/src/components/OceanHelmMaintenance.vue +1778 -0
- package/src/components/VesselList.vue +348 -0
- package/src/index.js +33 -0
- package/src/main.js +11 -0
- package/src/router/index.js +8 -0
- package/src/types/index.js +33 -0
- package/src/utils/permissions.js +10 -0
- package/src/utils/sidebarConfig.js +87 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- Fleet Summary -->
|
|
3
|
+
<div class="row mb-3">
|
|
4
|
+
<div class="col-md-4">
|
|
5
|
+
<div class="card border-0 shadow-sm">
|
|
6
|
+
<div class="card-body d-flex align-items-center">
|
|
7
|
+
<div class="rounded-circle bg-primary bg-opacity-10 p-3 me-3">
|
|
8
|
+
<i class="bi bi-check-circle-fill text-primary fs-4"></i>
|
|
9
|
+
</div>
|
|
10
|
+
<div>
|
|
11
|
+
<h6 class="mb-0">Active Vessels</h6>
|
|
12
|
+
<h3 class="mt-2 mb-0">{{ activeVesselsCount }}</h3>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="col-md-4">
|
|
18
|
+
<div class="card border-0 shadow-sm">
|
|
19
|
+
<div class="card-body d-flex align-items-center">
|
|
20
|
+
<div class="rounded-circle bg-secondary bg-opacity-10 p-3 me-3">
|
|
21
|
+
<i class="bi bi-pause-circle-fill text-secondary fs-4"></i>
|
|
22
|
+
</div>
|
|
23
|
+
<div>
|
|
24
|
+
<h6 class="mb-0">Inactive</h6>
|
|
25
|
+
<h3 class="mt-2 mb-0">{{ inactiveVesselsCount }}</h3>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<!-- Register New Fleet -->
|
|
31
|
+
<div class="col-md-4" v-if="canAddVessel">
|
|
32
|
+
<div class="card border-0 shadow-sm" @click="handleAddVessel">
|
|
33
|
+
<div class="card-body d-flex align-items-center">
|
|
34
|
+
<div class="rounded-circle bg-success bg-opacity-10 p-3 me-3">
|
|
35
|
+
<i class="bi bi-patch-plus-fill text-success fs-4"></i>
|
|
36
|
+
</div>
|
|
37
|
+
<div>
|
|
38
|
+
<h6 class="mb-0">Add New Vessel</h6>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<h4 class="mb-4"><i class="bi bi-ship me-2"></i>Registered Vessels</h4>
|
|
46
|
+
|
|
47
|
+
<!-- Loading State -->
|
|
48
|
+
<div v-if="loading" class="text-center py-4">
|
|
49
|
+
<div class="spinner-border text-primary" role="status">
|
|
50
|
+
<span class="visually-hidden">Loading...</span>
|
|
51
|
+
</div>
|
|
52
|
+
<p class="mt-2">Loading vessels...</p>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<!-- Empty State -->
|
|
56
|
+
<div v-else-if="!vessels.length" class="alert alert-primary" role="alert">
|
|
57
|
+
<h4 class="alert-heading">Empty Fleet!</h4>
|
|
58
|
+
<p>You have an empty fleet, you have not added any vessel yet.</p>
|
|
59
|
+
<hr>
|
|
60
|
+
<p class="mb-0">Click on the add vessel button above, to start adding vessels to your fleet</p>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<!-- Vessel Cards -->
|
|
64
|
+
<div v-else class="row">
|
|
65
|
+
<div class="col-lg-6" v-for="vessel in vessels" :key="vessel.registrationNumber || vessel.id">
|
|
66
|
+
<div class="vessel-card" @click="handleVesselClick(vessel)">
|
|
67
|
+
<div class="card-body d-flex align-items-center">
|
|
68
|
+
<div class="vessel-icon left">
|
|
69
|
+
<i class="fas fa-ship"></i>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="flex-grow-1">
|
|
72
|
+
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
73
|
+
<h5 class="card-title mb-0">{{ vessel.name }}</h5>
|
|
74
|
+
<span :class="['vessel-status', statusClass(vessel.status)]">{{ vessel.status }}</span>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="row">
|
|
77
|
+
<div class="col-6">
|
|
78
|
+
<small class="text-muted">Registration #:</small>
|
|
79
|
+
<p class="mb-0">{{ vessel.registrationNumber }}</p>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="col-6">
|
|
82
|
+
<small class="text-muted">Next Maintenance:</small>
|
|
83
|
+
<p class="mb-0">{{ vessel.nextMaintenance || 'Not scheduled' }}</p>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="action-icon left delete">
|
|
89
|
+
<i class="bi bi-trash" @click.stop="handleDeleteVessel(vessel)"></i>
|
|
90
|
+
</div>
|
|
91
|
+
<button class="btn btn-primary" @click.stop="handleToggleStatus(vessel)">
|
|
92
|
+
{{ vessel.status === 'Active' ? 'Mark Inactive' : 'Mark Active' }}
|
|
93
|
+
</button>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</template>
|
|
98
|
+
|
|
99
|
+
<script>
|
|
100
|
+
export default {
|
|
101
|
+
name: 'VesselList',
|
|
102
|
+
|
|
103
|
+
props: {
|
|
104
|
+
// Required props
|
|
105
|
+
vessels: {
|
|
106
|
+
type: Array,
|
|
107
|
+
required: true,
|
|
108
|
+
default: () => []
|
|
109
|
+
},
|
|
110
|
+
userProfile: {
|
|
111
|
+
type: Object,
|
|
112
|
+
required: true,
|
|
113
|
+
default: () => ({ role: 'viewer', vessel: null })
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
// Optional props
|
|
117
|
+
loading: {
|
|
118
|
+
type: Boolean,
|
|
119
|
+
default: false
|
|
120
|
+
},
|
|
121
|
+
currentRoute: {
|
|
122
|
+
type: String,
|
|
123
|
+
default: 'dashboard' // 'dashboard', 'maintenanceroute', 'crewroute'
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
// Configuration props
|
|
127
|
+
config: {
|
|
128
|
+
type: Object,
|
|
129
|
+
default: () => ({
|
|
130
|
+
enableAdd: true,
|
|
131
|
+
enableEdit: true,
|
|
132
|
+
enableDelete: true,
|
|
133
|
+
enableStatusToggle: true,
|
|
134
|
+
showCertifications: true
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
emits: [
|
|
140
|
+
'vessel-add',
|
|
141
|
+
'vessel-click',
|
|
142
|
+
'vessel-edit',
|
|
143
|
+
'vessel-delete',
|
|
144
|
+
'vessel-toggle-status',
|
|
145
|
+
'vessel-navigate',
|
|
146
|
+
'access-denied'
|
|
147
|
+
],
|
|
148
|
+
|
|
149
|
+
computed: {
|
|
150
|
+
activeVesselsCount() {
|
|
151
|
+
return this.vessels.filter(vessel => vessel.status === "Active").length;
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
inactiveVesselsCount() {
|
|
155
|
+
return this.vessels.filter(vessel => vessel.status === "Inactive").length;
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
canAddVessel() {
|
|
159
|
+
return this.config.enableAdd &&
|
|
160
|
+
(this.userProfile.role === 'owner' || this.userProfile.role === 'staff');
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
methods: {
|
|
165
|
+
// Event handlers that emit to parent
|
|
166
|
+
handleAddVessel() {
|
|
167
|
+
if (!this.canAddVessel) {
|
|
168
|
+
this.handleAccessDenied('add vessel');
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
this.$emit('vessel-add');
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
handleVesselClick(vessel) {
|
|
175
|
+
const navigationData = {
|
|
176
|
+
vessel,
|
|
177
|
+
route: this.currentRoute,
|
|
178
|
+
id: vessel.registrationNumber,
|
|
179
|
+
name: vessel.name
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
if (this.currentRoute === 'dashboard') {
|
|
183
|
+
this.$emit('vessel-click', vessel);
|
|
184
|
+
} else {
|
|
185
|
+
this.$emit('vessel-navigate', navigationData);
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
handleDeleteVessel(vessel) {
|
|
190
|
+
if (!this.grantAccess(vessel)) {
|
|
191
|
+
this.handleAccessDenied('delete vessel');
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!this.config.enableDelete) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
this.$emit('vessel-delete', vessel);
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
handleToggleStatus(vessel) {
|
|
203
|
+
if (!this.grantAccess(vessel)) {
|
|
204
|
+
this.handleAccessDenied('change vessel status');
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!this.config.enableStatusToggle) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
this.$emit('vessel-toggle-status', vessel);
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
handleEditVessel(vessel) {
|
|
216
|
+
if (!this.grantAccess(vessel)) {
|
|
217
|
+
this.handleAccessDenied('edit vessel');
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (!this.config.enableEdit) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
this.$emit('vessel-edit', vessel);
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
handleAccessDenied(action) {
|
|
229
|
+
this.$emit('access-denied', { action, userProfile: this.userProfile });
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
// Utility methods (keep these in component as they're UI-related)
|
|
233
|
+
statusClass(status) {
|
|
234
|
+
const normalized = status?.toLowerCase() || '';
|
|
235
|
+
return `status-${normalized}`;
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
grantAccess(vessel) {
|
|
239
|
+
const { role, vessel: userVessel } = this.userProfile;
|
|
240
|
+
|
|
241
|
+
return role === 'owner' ||
|
|
242
|
+
role === 'staff' ||
|
|
243
|
+
(role === 'captain' && userVessel === vessel.name);
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
getDaysToExpiry(expiry_date) {
|
|
247
|
+
if (!expiry_date) return null;
|
|
248
|
+
|
|
249
|
+
const today = new Date();
|
|
250
|
+
const expiry = new Date(expiry_date);
|
|
251
|
+
const diffTime = expiry - today;
|
|
252
|
+
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
253
|
+
return diffDays;
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
getExpiryClass(expiry_date) {
|
|
257
|
+
const days = this.getDaysToExpiry(expiry_date);
|
|
258
|
+
if (days === null) return '';
|
|
259
|
+
if (days < 30) return 'cert-critical';
|
|
260
|
+
if (days < 90) return 'cert-warning';
|
|
261
|
+
return '';
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
</script>
|
|
266
|
+
|
|
267
|
+
<style>
|
|
268
|
+
.left {
|
|
269
|
+
margin-left: 20px;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.vessel-card {
|
|
273
|
+
background: white;
|
|
274
|
+
border-radius: 10px;
|
|
275
|
+
box-shadow: 0 8px 16px rgba(0, 105, 192, 0.15);
|
|
276
|
+
transition: all 0.3s ease;
|
|
277
|
+
margin-bottom: 20px;
|
|
278
|
+
overflow: hidden;
|
|
279
|
+
border-left: 4px solid var(--accent-color);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.vessel-card:hover {
|
|
283
|
+
transform: translateY(-5px);
|
|
284
|
+
box-shadow: 0 12px 20px rgba(0, 105, 192, 0.2);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.vessel-icon {
|
|
288
|
+
display: inline-flex;
|
|
289
|
+
align-items: center;
|
|
290
|
+
justify-content: center;
|
|
291
|
+
width: 50px;
|
|
292
|
+
height: 50px;
|
|
293
|
+
background-color: #e3f2fd;
|
|
294
|
+
border-radius: 10px;
|
|
295
|
+
color: var(--accent-color);
|
|
296
|
+
font-size: 24px;
|
|
297
|
+
margin-right: 15px;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.black {
|
|
301
|
+
color: black !important;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.action-icon {
|
|
305
|
+
display: inline-flex;
|
|
306
|
+
align-items: center;
|
|
307
|
+
justify-content: center;
|
|
308
|
+
width: 30px;
|
|
309
|
+
height: 30px;
|
|
310
|
+
border-radius: 20px;
|
|
311
|
+
color: var(--accent-color);
|
|
312
|
+
font-size: 16px;
|
|
313
|
+
margin-right: 15px;
|
|
314
|
+
margin-top: 20px;
|
|
315
|
+
margin-bottom: 10px;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.delete {
|
|
319
|
+
background-color: var(--danger);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.edit {
|
|
323
|
+
background-color: var(--success);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.vessel-status {
|
|
327
|
+
display: inline-block;
|
|
328
|
+
padding: 5px 12px;
|
|
329
|
+
border-radius: 20px;
|
|
330
|
+
font-size: 12px;
|
|
331
|
+
font-weight: 600;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.status-active {
|
|
335
|
+
background-color: #e8f5e9;
|
|
336
|
+
color: #2e7d32;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.status-maintenance {
|
|
340
|
+
background-color: #fff8e1;
|
|
341
|
+
color: #f57f17;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.status-inactive {
|
|
345
|
+
background-color: #f5f5f5;
|
|
346
|
+
color: #757575;
|
|
347
|
+
}
|
|
348
|
+
</style>
|
package/src/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/index.js - Library entry point
|
|
2
|
+
import ConfigurableSidebar from './components/ConfigurableSidebar.vue'
|
|
3
|
+
import VesselList from './components/VesselList.vue'
|
|
4
|
+
import DashHead from './components/DashHead.vue'
|
|
5
|
+
import OceanHelmMaintenance from './components/OceanHelmMaintenance.vue'
|
|
6
|
+
import ActivityLogs from './components/ActivityLogs.vue'
|
|
7
|
+
import CrewManagement from './components/CrewManagement.vue'
|
|
8
|
+
import { createSidebarConfig, defaultMenuItems } from './utils/sidebarConfig'
|
|
9
|
+
import { defaultPermissionChecker } from './utils/permissions'
|
|
10
|
+
|
|
11
|
+
// Export main components
|
|
12
|
+
export { ConfigurableSidebar, VesselList, DashHead, CrewManagement, ActivityLogs, OceanHelmMaintenance }
|
|
13
|
+
|
|
14
|
+
// Export utilities
|
|
15
|
+
export { createSidebarConfig, defaultMenuItems, defaultPermissionChecker }
|
|
16
|
+
|
|
17
|
+
// Export types for TypeScript users
|
|
18
|
+
export * from './types'
|
|
19
|
+
|
|
20
|
+
// Default export for plugin-style usage
|
|
21
|
+
export default {
|
|
22
|
+
install(app, options = {}) {
|
|
23
|
+
app.component('ConfigurableSidebar', ConfigurableSidebar)
|
|
24
|
+
app.component('VesselList', VesselList)
|
|
25
|
+
app.component('DashHead', DashHead)
|
|
26
|
+
app.component('ActivityLogs', ActivityLogs)
|
|
27
|
+
app.component('CrewManagement', CrewManagement)
|
|
28
|
+
app.component('DashHead', OceanHelmMaintenance)
|
|
29
|
+
|
|
30
|
+
// Provide global configuration
|
|
31
|
+
app.provide('sidebarConfig', options)
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/main.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// ===== src/types/index.js (JSDoc type definitions) =====
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} MenuItem
|
|
5
|
+
* @property {'link'|'button'|'dropdown'|'text'|'separator'} type - Type of menu item
|
|
6
|
+
* @property {string} [label] - Display label for the item
|
|
7
|
+
* @property {string} [icon] - CSS class for icon
|
|
8
|
+
* @property {string} [href] - Link URL for 'link' type items
|
|
9
|
+
* @property {string} [action] - Action identifier for 'button' type items
|
|
10
|
+
* @property {string[]} [roles] - Required user roles to see this item
|
|
11
|
+
* @property {boolean} [active] - Whether item is currently active
|
|
12
|
+
* @property {boolean} [external] - Whether link opens in new tab
|
|
13
|
+
* @property {boolean} [preventDefault] - Whether to prevent default navigation
|
|
14
|
+
* @property {MenuItem[]} [children] - Child items for dropdown type
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {Object} UserProfile
|
|
19
|
+
* @property {string} role - User's role
|
|
20
|
+
* @property {*} [key] - Any additional user properties
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {Object} SidebarConfig
|
|
25
|
+
* @property {string} [brandName] - Brand name to display
|
|
26
|
+
* @property {string} [logoIcon] - CSS class for logo icon
|
|
27
|
+
* @property {boolean} [showLogo] - Whether to show logo section
|
|
28
|
+
* @property {boolean} [responsive] - Whether to enable responsive behavior
|
|
29
|
+
* @property {string} [customClasses] - Additional CSS classes
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
// Export empty object to make this a module
|
|
33
|
+
export {}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// ===== src/utils/permissions.js =====
|
|
2
|
+
export const defaultPermissionChecker = (item, userProfile) => {
|
|
3
|
+
// No role restrictions
|
|
4
|
+
if (!item.roles || item.roles.length === 0) {
|
|
5
|
+
return true
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Check if user role is in allowed roles
|
|
9
|
+
return item.roles.includes(userProfile?.role)
|
|
10
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// ===== src/utils/sidebarConfig.js =====
|
|
2
|
+
export const createSidebarConfig = (options = {}) => {
|
|
3
|
+
return {
|
|
4
|
+
brandName: options.brandName || 'OceanHelm',
|
|
5
|
+
logoIcon: options.logoIcon || 'bi bi-water',
|
|
6
|
+
showLogo: options.showLogo !== false,
|
|
7
|
+
responsive: options.responsive !== false,
|
|
8
|
+
...options
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const defaultMenuItems = [
|
|
13
|
+
{
|
|
14
|
+
type: 'link',
|
|
15
|
+
label: 'Dashboard',
|
|
16
|
+
icon: 'bi bi-speedometer2',
|
|
17
|
+
href: '/app/dashboard',
|
|
18
|
+
active: true
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
type: 'link',
|
|
22
|
+
label: 'Activity Log',
|
|
23
|
+
icon: 'bi bi-activity',
|
|
24
|
+
href: '/activity-log',
|
|
25
|
+
roles: ['owner'] // Role-based visibility
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: 'text',
|
|
29
|
+
label: 'Services'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: 'button',
|
|
33
|
+
label: 'Maintenance',
|
|
34
|
+
icon: 'bi bi-tools',
|
|
35
|
+
action: 'maintenance'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: 'dropdown',
|
|
39
|
+
label: 'Crew Management',
|
|
40
|
+
icon: 'bi bi-people',
|
|
41
|
+
children: [
|
|
42
|
+
{
|
|
43
|
+
label: 'All Crew',
|
|
44
|
+
action: 'crew-all',
|
|
45
|
+
roles: ['owner', 'staff']
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: 'separator'
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
label: 'Get Crew by Vessel',
|
|
52
|
+
action: 'crew-by-vessel'
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: 'link',
|
|
58
|
+
label: 'Inventory Management',
|
|
59
|
+
icon: 'bi bi-clipboard-data',
|
|
60
|
+
href: '/app/inventory'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: 'link',
|
|
64
|
+
label: 'Requisition Processing',
|
|
65
|
+
icon: 'bi bi-calendar-check',
|
|
66
|
+
href: '/app/requisition'
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: 'button',
|
|
70
|
+
label: 'Voyage Manager',
|
|
71
|
+
icon: 'fas fa-ship',
|
|
72
|
+
action: 'coming-soon'
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
type: 'button',
|
|
76
|
+
label: 'Settings',
|
|
77
|
+
icon: 'bi bi-gear',
|
|
78
|
+
action: 'settings',
|
|
79
|
+
roles: ['owner', 'staff']
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
type: 'button',
|
|
83
|
+
label: 'Help & Support',
|
|
84
|
+
icon: 'bi bi-question-circle',
|
|
85
|
+
action: 'help'
|
|
86
|
+
}
|
|
87
|
+
]
|