oceanhelm 0.0.11 → 0.0.13
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 +3211 -1416
- package/dist/oceanhelm.es.js.map +1 -1
- package/dist/oceanhelm.umd.js +9 -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 +2985 -428
- package/src/components/RequisitionSystem.vue +97 -67
- package/src/utils/sidebarConfig.js +62 -48
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<div class="form-grid">
|
|
20
20
|
<div class="form-group">
|
|
21
21
|
<label>Requestor Name *</label>
|
|
22
|
-
<input type="text" :value="userProfile
|
|
22
|
+
<input type="text" :value="userProfile?.full_name" readonly required class="form-control" />
|
|
23
23
|
</div>
|
|
24
24
|
<div class="form-group">
|
|
25
25
|
<label>Department *</label>
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
</div>
|
|
40
40
|
<div class="form-group">
|
|
41
41
|
<label>Date Needed *</label>
|
|
42
|
-
<input type="date" v-model="form.neededDate" required />
|
|
42
|
+
<input type="date" v-model="form.neededDate" :min="today" required />
|
|
43
43
|
</div>
|
|
44
44
|
</div>
|
|
45
45
|
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
</div>
|
|
77
77
|
<div class="form-group">
|
|
78
78
|
<label>Est. Unit Cost *</label>
|
|
79
|
-
<input type="number" v-model.number="item.cost" step="0.01" placeholder="0.00" required />
|
|
79
|
+
<input type="number" v-model.number="item.cost" step="0.01" min="0" placeholder="0.00" required />
|
|
80
80
|
</div>
|
|
81
81
|
<button type="button" class="remove-item-btn" @click="removeItem(index)">Remove</button>
|
|
82
82
|
</div>
|
|
@@ -84,7 +84,9 @@
|
|
|
84
84
|
</div>
|
|
85
85
|
|
|
86
86
|
<div class="action-buttons">
|
|
87
|
-
<button type="submit" class="btn btn-primary-req"
|
|
87
|
+
<button type="submit" class="btn btn-primary-req" :disabled="isSubmitting">
|
|
88
|
+
{{ isSubmitting ? 'Submitting...' : 'Submit Requisition' }}
|
|
89
|
+
</button>
|
|
88
90
|
</div>
|
|
89
91
|
</form>
|
|
90
92
|
</div>
|
|
@@ -270,23 +272,23 @@
|
|
|
270
272
|
<h3>Vendor Information</h3>
|
|
271
273
|
<div class="info-row">
|
|
272
274
|
<span class="info-label">Company:</span>
|
|
273
|
-
<span class="info-value">{{ vendorInfo.company || poDetails.vendorInfo?.company }} </span>
|
|
275
|
+
<span class="info-value">{{ vendorInfo.company || poDetails.vendorInfo?.company || 'N/A' }} </span>
|
|
274
276
|
</div>
|
|
275
277
|
<div class="info-row">
|
|
276
278
|
<span class="info-label">Contact:</span>
|
|
277
|
-
<span class="info-value">{{ vendorInfo.contact || poDetails.vendorInfo?.contact }}</span>
|
|
279
|
+
<span class="info-value">{{ vendorInfo.contact || poDetails.vendorInfo?.contact || 'N/A' }}</span>
|
|
278
280
|
</div>
|
|
279
281
|
<div class="info-row">
|
|
280
282
|
<span class="info-label">Email:</span>
|
|
281
|
-
<span class="info-value">{{ vendorInfo.email || poDetails.vendorInfo?.email }}</span>
|
|
283
|
+
<span class="info-value">{{ vendorInfo.email || poDetails.vendorInfo?.email || 'N/A' }}</span>
|
|
282
284
|
</div>
|
|
283
285
|
<div class="info-row">
|
|
284
286
|
<span class="info-label">Phone:</span>
|
|
285
|
-
<span class="info-
|
|
287
|
+
<span class="info-label">{{ vendorInfo.phone || poDetails.vendorInfo?.phone || 'N/A' }}</span>
|
|
286
288
|
</div>
|
|
287
289
|
<div class="info-row">
|
|
288
290
|
<span class="info-label">Address:</span>
|
|
289
|
-
<span class="info-value">{{ vendorInfo.address || poDetails.vendorInfo?.address }}</span>
|
|
291
|
+
<span class="info-value">{{ vendorInfo.address || poDetails.vendorInfo?.address || 'N/A' }}</span>
|
|
290
292
|
</div>
|
|
291
293
|
</div>
|
|
292
294
|
|
|
@@ -298,19 +300,19 @@
|
|
|
298
300
|
</div>
|
|
299
301
|
<div class="info-row">
|
|
300
302
|
<span class="info-label">Date:</span>
|
|
301
|
-
<span class="info-value">{{ vendorInfo.poDate || poDetails.vendorInfo?.poDate }}</span>
|
|
303
|
+
<span class="info-value">{{ vendorInfo.poDate || poDetails.vendorInfo?.poDate || formatDate(new Date()) }}</span>
|
|
302
304
|
</div>
|
|
303
305
|
<div class="info-row">
|
|
304
306
|
<span class="info-label">Requested By:</span>
|
|
305
|
-
<span class="info-value">{{ vendorInfo.poApproved || poDetails.vendorInfo?.poApproved }}</span>
|
|
307
|
+
<span class="info-value">{{ vendorInfo.poApproved || poDetails.vendorInfo?.poApproved || 'N/A' }}</span>
|
|
306
308
|
</div>
|
|
307
309
|
<div class="info-row">
|
|
308
310
|
<span class="info-label">Department:</span>
|
|
309
|
-
<span class="info-value">PO-{{ poDetails.department }}</span>
|
|
311
|
+
<span class="info-value">PO-{{ poDetails.department || 'N/A' }}</span>
|
|
310
312
|
</div>
|
|
311
313
|
<div class="info-row">
|
|
312
314
|
<span class="info-label">Delivery Date:</span>
|
|
313
|
-
<span class="info-value">{{ poDetails.neededDate }}</span>
|
|
315
|
+
<span class="info-value">{{ poDetails.neededDate || 'N/A' }}</span>
|
|
314
316
|
</div>
|
|
315
317
|
</div>
|
|
316
318
|
</div>
|
|
@@ -330,18 +332,18 @@
|
|
|
330
332
|
</thead>
|
|
331
333
|
<tbody>
|
|
332
334
|
<tr v-for="(item, index) in poDetails.items || []" :key="index">
|
|
333
|
-
<td>{{ item.itemNumber }}</td>
|
|
335
|
+
<td>{{ item.itemNumber || (index + 1) }}</td>
|
|
334
336
|
<td>{{ item.desc }}</td>
|
|
335
337
|
<td>{{ item.qty }}</td>
|
|
336
338
|
<td>
|
|
337
339
|
<span v-if="!item.editing">
|
|
338
|
-
${{ item.cost.toFixed(2) }}
|
|
339
|
-
<span v-if="item.cost !== item.unitPrice" class="price-change-indicator">!</span>
|
|
340
|
+
${{ (item.unitPrice || item.cost || 0).toFixed(2) }}
|
|
341
|
+
<span v-if="item.cost !== item.unitPrice && item.unitPrice" class="price-change-indicator">!</span>
|
|
340
342
|
</span>
|
|
341
|
-
<input v-else v-model.number="item.tempPrice" type="number" step="0.01"
|
|
343
|
+
<input v-else v-model.number="item.tempPrice" type="number" step="0.01" min="0"
|
|
342
344
|
class="price-input" :class="{ 'price-changed': item.cost !== item.tempPrice }">
|
|
343
345
|
</td>
|
|
344
|
-
<td>${{ (item.unitPrice * item.qty).toFixed(2) }}</td>
|
|
346
|
+
<td>${{ ((item.unitPrice || item.cost || 0) * (item.qty || 0)).toFixed(2) }}</td>
|
|
345
347
|
<td v-if="!isPrinting">
|
|
346
348
|
<button v-if="!item.editing" @click="startEdit(index)" class="edit-btn">
|
|
347
349
|
Edit Price
|
|
@@ -357,7 +359,7 @@
|
|
|
357
359
|
|
|
358
360
|
<div v-for="(item, index) in poDetails.items || []" :key="'note-' + index">
|
|
359
361
|
<div v-if="item.justification" class="justification-note">
|
|
360
|
-
<strong>Price Change Justification (Item {{ item.itemNumber }}):</strong> {{ item.justification
|
|
362
|
+
<strong>Price Change Justification (Item {{ item.itemNumber || (index + 1) }}):</strong> {{ item.justification
|
|
361
363
|
}}
|
|
362
364
|
</div>
|
|
363
365
|
</div>
|
|
@@ -368,8 +370,8 @@
|
|
|
368
370
|
<span>${{ subTotal.toFixed(2) }}</span>
|
|
369
371
|
</div>
|
|
370
372
|
<div class="total-row">
|
|
371
|
-
<span>Tax (%):</span>
|
|
372
|
-
<span>${{
|
|
373
|
+
<span>Tax ({{ vendorInfo.tax || poDetails.vendorInfo?.tax || 0 }}%):</span>
|
|
374
|
+
<span>${{ calculateTax().toFixed(2) }}</span>
|
|
373
375
|
</div>
|
|
374
376
|
<div class="total-row">
|
|
375
377
|
<span>Shipping:</span>
|
|
@@ -377,8 +379,7 @@
|
|
|
377
379
|
</div>
|
|
378
380
|
<div class="total-row grand-total">
|
|
379
381
|
<span>Grand Total:</span>
|
|
380
|
-
<span>${{
|
|
381
|
-
}}</span>
|
|
382
|
+
<span>${{ grandTotal.toFixed(2) }}</span>
|
|
382
383
|
</div>
|
|
383
384
|
</div>
|
|
384
385
|
</div>
|
|
@@ -499,6 +500,7 @@ export default {
|
|
|
499
500
|
activeTab: 'workflow',
|
|
500
501
|
isPrinting: false,
|
|
501
502
|
infoResponse: '',
|
|
503
|
+
isSubmitting: false,
|
|
502
504
|
|
|
503
505
|
// Tabs with visibility rules
|
|
504
506
|
tabs: [
|
|
@@ -536,7 +538,7 @@ export default {
|
|
|
536
538
|
{ label: 'Department', value: req => req.department },
|
|
537
539
|
{ label: 'Project', value: req => req.project || 'N/A' },
|
|
538
540
|
{ label: 'Submitted', value: req => req.submittedDate },
|
|
539
|
-
{ label: 'Items', value: req => `${req.items
|
|
541
|
+
{ label: 'Items', value: req => `${req.items?.length || 0} item(s)` }
|
|
540
542
|
],
|
|
541
543
|
|
|
542
544
|
// Fields for My Requisitions display
|
|
@@ -573,26 +575,35 @@ export default {
|
|
|
573
575
|
},
|
|
574
576
|
|
|
575
577
|
computed: {
|
|
578
|
+
today() {
|
|
579
|
+
return new Date().toISOString().split('T')[0];
|
|
580
|
+
},
|
|
581
|
+
|
|
576
582
|
visibleTabs() {
|
|
577
|
-
return this.tabs.filter(tab => tab.roles.includes(this.userRole))
|
|
583
|
+
return this.tabs.filter(tab => tab.roles.includes(this.userRole));
|
|
578
584
|
},
|
|
579
585
|
|
|
580
586
|
subTotal() {
|
|
581
587
|
return (this.poDetails.items || []).reduce((sum, item) => {
|
|
582
|
-
|
|
588
|
+
const unitPrice = item.unitPrice || item.cost || 0;
|
|
589
|
+
return sum + (unitPrice * (item.qty || 0));
|
|
583
590
|
}, 0);
|
|
584
591
|
},
|
|
585
592
|
|
|
593
|
+
grandTotal() {
|
|
594
|
+
return this.subTotal + this.calculateTax() + parseFloat(this.getOptional(this.vendorInfo.shipping));
|
|
595
|
+
},
|
|
596
|
+
|
|
586
597
|
reviewRequisitions() {
|
|
587
|
-
return this.requisitions.filter(r => r.status === 'under-review');
|
|
598
|
+
return (this.requisitions || []).filter(r => r.status === 'under-review');
|
|
588
599
|
},
|
|
589
600
|
|
|
590
601
|
poRequisitions() {
|
|
591
|
-
return this.requisitions.filter(r => r.status === 'approved');
|
|
602
|
+
return (this.requisitions || []).filter(r => r.status === 'approved');
|
|
592
603
|
},
|
|
593
604
|
|
|
594
605
|
awaitingDelivery() {
|
|
595
|
-
return this.requisitions.filter(r => r.status === 'po-created');
|
|
606
|
+
return (this.requisitions || []).filter(r => r.status === 'po-created');
|
|
596
607
|
},
|
|
597
608
|
|
|
598
609
|
currentItem() {
|
|
@@ -600,8 +611,8 @@ export default {
|
|
|
600
611
|
},
|
|
601
612
|
|
|
602
613
|
myRequisitions() {
|
|
603
|
-
const userId = this.userProfile
|
|
604
|
-
return this.requisitions.filter(req => req.profile_id == userId);
|
|
614
|
+
const userId = this.userProfile?.id || this.userProfile?.profile_id;
|
|
615
|
+
return (this.requisitions || []).filter(req => req.profile_id == userId);
|
|
605
616
|
}
|
|
606
617
|
},
|
|
607
618
|
|
|
@@ -610,8 +621,19 @@ export default {
|
|
|
610
621
|
this.activeTab = tabName;
|
|
611
622
|
},
|
|
612
623
|
|
|
624
|
+
formatDate(dateString) {
|
|
625
|
+
if (!dateString) return 'N/A';
|
|
626
|
+
const date = new Date(dateString);
|
|
627
|
+
return date.toLocaleDateString();
|
|
628
|
+
},
|
|
629
|
+
|
|
630
|
+
calculateTax() {
|
|
631
|
+
const taxRate = parseFloat(this.vendorInfo.tax || this.poDetails.vendorInfo?.tax || 0);
|
|
632
|
+
return (this.subTotal * taxRate) / 100;
|
|
633
|
+
},
|
|
634
|
+
|
|
613
635
|
getOptional(value) {
|
|
614
|
-
return typeof value === 'number' ? value.toFixed(2) :
|
|
636
|
+
return typeof value === 'number' ? value.toFixed(2) : (parseFloat(value) || 0).toFixed(2);
|
|
615
637
|
},
|
|
616
638
|
|
|
617
639
|
getRequisitionFields(req) {
|
|
@@ -626,21 +648,21 @@ export default {
|
|
|
626
648
|
qty: 1,
|
|
627
649
|
unit: 'Pieces',
|
|
628
650
|
cost: 0
|
|
629
|
-
})
|
|
651
|
+
});
|
|
630
652
|
},
|
|
631
653
|
|
|
632
654
|
removeItem(index) {
|
|
633
|
-
this.form.items.splice(index, 1)
|
|
655
|
+
this.form.items.splice(index, 1);
|
|
634
656
|
},
|
|
635
657
|
|
|
636
658
|
startEdit(index) {
|
|
637
659
|
this.poDetails.items[index].editing = true;
|
|
638
|
-
this.poDetails.items[index].tempPrice = this.poDetails.items[index].unitPrice;
|
|
660
|
+
this.poDetails.items[index].tempPrice = this.poDetails.items[index].unitPrice || this.poDetails.items[index].cost;
|
|
639
661
|
},
|
|
640
662
|
|
|
641
663
|
cancelEdit(index) {
|
|
642
664
|
this.poDetails.items[index].editing = false;
|
|
643
|
-
this.poDetails.items[index].tempPrice = this.poDetails.items[index].unitPrice;
|
|
665
|
+
this.poDetails.items[index].tempPrice = this.poDetails.items[index].unitPrice || this.poDetails.items[index].cost;
|
|
644
666
|
},
|
|
645
667
|
|
|
646
668
|
savePrice(index) {
|
|
@@ -679,9 +701,19 @@ export default {
|
|
|
679
701
|
|
|
680
702
|
// Event handlers that emit to parent
|
|
681
703
|
handleSubmitRequisition() {
|
|
704
|
+
if (this.form.items.length === 0) {
|
|
705
|
+
alert('Please add at least one item to the requisition.');
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
this.isSubmitting = true;
|
|
682
710
|
const requisition = this.collectFormData('under-review');
|
|
683
711
|
this.$emit('submit-requisition', requisition);
|
|
684
|
-
|
|
712
|
+
|
|
713
|
+
setTimeout(() => {
|
|
714
|
+
this.resetForm();
|
|
715
|
+
this.isSubmitting = false;
|
|
716
|
+
}, 1000);
|
|
685
717
|
},
|
|
686
718
|
|
|
687
719
|
handleSubmitInfoResponse(req) {
|
|
@@ -724,7 +756,7 @@ export default {
|
|
|
724
756
|
collectFormData(status) {
|
|
725
757
|
return {
|
|
726
758
|
id: 'REQ-' + Date.now(),
|
|
727
|
-
requestor: this.userProfile
|
|
759
|
+
requestor: this.userProfile?.full_name || 'Unknown',
|
|
728
760
|
department: this.form.department,
|
|
729
761
|
project: this.form.project,
|
|
730
762
|
neededDate: this.form.neededDate,
|
|
@@ -732,8 +764,8 @@ export default {
|
|
|
732
764
|
items: this.form.items.map(item => ({ ...item })),
|
|
733
765
|
status,
|
|
734
766
|
submittedDate: new Date().toLocaleDateString(),
|
|
735
|
-
profile_id: this.userProfile
|
|
736
|
-
}
|
|
767
|
+
profile_id: this.userProfile?.id || this.userProfile?.profile_id
|
|
768
|
+
};
|
|
737
769
|
},
|
|
738
770
|
|
|
739
771
|
resetForm() {
|
|
@@ -744,24 +776,24 @@ export default {
|
|
|
744
776
|
neededDate: '',
|
|
745
777
|
justification: '',
|
|
746
778
|
items: []
|
|
747
|
-
}
|
|
779
|
+
};
|
|
748
780
|
// Add initial item row again
|
|
749
|
-
this.addItem()
|
|
781
|
+
this.addItem();
|
|
750
782
|
},
|
|
751
783
|
|
|
752
784
|
// Methods for updating PO details from parent
|
|
753
785
|
updatePODetails(details) {
|
|
754
|
-
this.poDetails = details;
|
|
786
|
+
this.poDetails = { ...details };
|
|
755
787
|
this.getNumber();
|
|
756
|
-
for (let item of this.poDetails.items) {
|
|
757
|
-
item.tempPrice = item.cost;
|
|
758
|
-
item.unitPrice = item.cost;
|
|
759
|
-
item.subTotal = item.unitPrice * item.qty;
|
|
788
|
+
for (let item of this.poDetails.items || []) {
|
|
789
|
+
item.tempPrice = item.cost || 0;
|
|
790
|
+
item.unitPrice = item.unitPrice || item.cost || 0;
|
|
791
|
+
item.subTotal = item.unitPrice * (item.qty || 0);
|
|
760
792
|
}
|
|
761
793
|
},
|
|
762
794
|
|
|
763
795
|
updateVendorInfo(info) {
|
|
764
|
-
this.vendorInfo = info;
|
|
796
|
+
this.vendorInfo = { ...info };
|
|
765
797
|
},
|
|
766
798
|
|
|
767
799
|
setPrintingMode(isPrinting) {
|
|
@@ -771,13 +803,13 @@ export default {
|
|
|
771
803
|
getNumber() {
|
|
772
804
|
return (this.poDetails.items || []).map((item, index) => {
|
|
773
805
|
return item.itemNumber = index + 1;
|
|
774
|
-
})
|
|
806
|
+
});
|
|
775
807
|
}
|
|
776
808
|
},
|
|
777
809
|
|
|
778
810
|
created() {
|
|
779
811
|
// Initialize form with one item row
|
|
780
|
-
this.addItem()
|
|
812
|
+
this.addItem();
|
|
781
813
|
}
|
|
782
814
|
}
|
|
783
815
|
</script>
|
|
@@ -814,16 +846,11 @@ export default {
|
|
|
814
846
|
background: #f8fafc;
|
|
815
847
|
border-bottom: 2px solid #e2e8f0;
|
|
816
848
|
overflow-x: auto;
|
|
817
|
-
/* enable horizontal scroll */
|
|
818
849
|
white-space: nowrap;
|
|
819
|
-
/* prevent wrapping */
|
|
820
850
|
-webkit-overflow-scrolling: touch;
|
|
821
|
-
/* smooth scrolling on mobile */
|
|
822
851
|
scrollbar-width: thin;
|
|
823
|
-
/* optional: thinner scrollbar in Firefox */
|
|
824
852
|
}
|
|
825
853
|
|
|
826
|
-
/* Optional: style the scrollbar (Webkit browsers only) */
|
|
827
854
|
.nav-tabs::-webkit-scrollbar {
|
|
828
855
|
height: 6px;
|
|
829
856
|
}
|
|
@@ -1002,14 +1029,11 @@ export default {
|
|
|
1002
1029
|
|
|
1003
1030
|
.item-list li {
|
|
1004
1031
|
background: #f1f5f9;
|
|
1005
|
-
/* Light blue-gray background */
|
|
1006
1032
|
margin-bottom: 0.5rem;
|
|
1007
1033
|
padding: 0.5rem 0.75rem;
|
|
1008
1034
|
border-radius: 0.375rem;
|
|
1009
|
-
/* Rounded corners */
|
|
1010
1035
|
font-size: 0.95rem;
|
|
1011
1036
|
color: #1e293b;
|
|
1012
|
-
/* Dark text */
|
|
1013
1037
|
display: flex;
|
|
1014
1038
|
justify-content: space-between;
|
|
1015
1039
|
align-items: center;
|
|
@@ -1276,7 +1300,10 @@ export default {
|
|
|
1276
1300
|
border-top: 2px solid #e5e7eb;
|
|
1277
1301
|
}
|
|
1278
1302
|
|
|
1279
|
-
|
|
1303
|
+
.btn-primary-req {
|
|
1304
|
+
background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%);
|
|
1305
|
+
color: white;
|
|
1306
|
+
box-shadow: 0 4px 15px rgba(30, 64, 175, 0.3);
|
|
1280
1307
|
padding: 12px 25px;
|
|
1281
1308
|
border: none;
|
|
1282
1309
|
border-radius: 10px;
|
|
@@ -1284,20 +1311,18 @@ export default {
|
|
|
1284
1311
|
font-weight: 600;
|
|
1285
1312
|
cursor: pointer;
|
|
1286
1313
|
transition: all 0.3s ease;
|
|
1287
|
-
min-width: 120px;
|
|
1288
|
-
}*/
|
|
1289
|
-
|
|
1290
|
-
.btn-primary-req {
|
|
1291
|
-
background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%);
|
|
1292
|
-
color: white;
|
|
1293
|
-
box-shadow: 0 4px 15px rgba(30, 64, 175, 0.3);
|
|
1294
1314
|
}
|
|
1295
1315
|
|
|
1296
|
-
.btn-primary-req:hover {
|
|
1316
|
+
.btn-primary-req:hover:not(:disabled) {
|
|
1297
1317
|
transform: translateY(-2px);
|
|
1298
1318
|
box-shadow: 0 6px 20px rgba(30, 64, 175, 0.4);
|
|
1299
1319
|
}
|
|
1300
1320
|
|
|
1321
|
+
.btn-primary-req:disabled {
|
|
1322
|
+
opacity: 0.6;
|
|
1323
|
+
cursor: not-allowed;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1301
1326
|
.btn-secondary {
|
|
1302
1327
|
background: #6b7280;
|
|
1303
1328
|
color: white;
|
|
@@ -1597,6 +1622,11 @@ export default {
|
|
|
1597
1622
|
margin-right: 5px;
|
|
1598
1623
|
}
|
|
1599
1624
|
|
|
1625
|
+
.save-btn:disabled {
|
|
1626
|
+
background: #95a5a6;
|
|
1627
|
+
cursor: not-allowed;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1600
1630
|
.cancel-btn {
|
|
1601
1631
|
background: #e74c3c;
|
|
1602
1632
|
color: white;
|
|
@@ -13,7 +13,7 @@ export const defaultMenuItems = [
|
|
|
13
13
|
{
|
|
14
14
|
type: 'link',
|
|
15
15
|
label: 'Home',
|
|
16
|
-
icon: 'bi bi-
|
|
16
|
+
icon: 'bi bi-house',
|
|
17
17
|
roles: ['owner', 'staff', 'captain'],
|
|
18
18
|
href: '/app/dashboard',
|
|
19
19
|
active: true
|
|
@@ -23,22 +23,21 @@ export const defaultMenuItems = [
|
|
|
23
23
|
label: 'Guide',
|
|
24
24
|
icon: 'bi bi-compass',
|
|
25
25
|
action: 'guide',
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
roles: ['crew']
|
|
28
27
|
},
|
|
29
28
|
{
|
|
30
29
|
type: 'link',
|
|
31
30
|
label: 'Activity Log',
|
|
32
31
|
icon: 'bi bi-activity',
|
|
33
32
|
href: '/activity-log',
|
|
34
|
-
roles: ['owner']
|
|
33
|
+
roles: ['owner']
|
|
35
34
|
},
|
|
36
35
|
{
|
|
37
36
|
type: 'link',
|
|
38
37
|
label: 'People Manager',
|
|
39
38
|
icon: 'bi bi-person-plus',
|
|
40
39
|
href: '/app/people-manager',
|
|
41
|
-
roles: ['owner']
|
|
40
|
+
roles: ['owner']
|
|
42
41
|
},
|
|
43
42
|
{
|
|
44
43
|
type: 'text',
|
|
@@ -49,28 +48,51 @@ export const defaultMenuItems = [
|
|
|
49
48
|
label: 'Compliance',
|
|
50
49
|
roles: ['owner', 'staff', 'captain'],
|
|
51
50
|
icon: 'bi bi-list-ul',
|
|
51
|
+
children: [
|
|
52
|
+
{ label: 'Vessel Certification', action: 'vessel-cert' },
|
|
53
|
+
{ label: 'Reports', action: 'reports' },
|
|
54
|
+
{ label: 'Crew Certification', action: 'crew-cert' },
|
|
55
|
+
{ label: 'QHSE Compliance', action: 'qhse' },
|
|
56
|
+
{ label: 'Track Garbage', action: 'garbage' },
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
// ── HR MODULE ──────────────────────────────────────────────────────────
|
|
61
|
+
// Clock-in/out is office-only (owner + staff roles).
|
|
62
|
+
// Payroll and appraisal will also cover crew in a later release.
|
|
63
|
+
type: 'dropdown',
|
|
64
|
+
label: 'HR',
|
|
65
|
+
icon: 'bi bi-people-fill',
|
|
66
|
+
roles: ['owner', 'staff'],
|
|
52
67
|
children: [
|
|
53
68
|
{
|
|
54
|
-
label: '
|
|
55
|
-
action: '
|
|
69
|
+
label: 'Attendance',
|
|
70
|
+
action: 'hr-attendance',
|
|
71
|
+
roles: ['owner', 'staff'],
|
|
56
72
|
},
|
|
57
73
|
{
|
|
58
|
-
|
|
74
|
+
label: 'Payroll',
|
|
75
|
+
action: 'hr-payroll',
|
|
76
|
+
roles: ['owner', 'staff'],
|
|
59
77
|
},
|
|
60
78
|
{
|
|
61
|
-
label: '
|
|
62
|
-
action: '
|
|
79
|
+
label: 'Appraisals',
|
|
80
|
+
action: 'hr-appraisals',
|
|
81
|
+
roles: ['owner', 'staff'],
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
label: 'Leave Management',
|
|
85
|
+
action: 'hr-leave',
|
|
86
|
+
roles: ['owner', 'staff'],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
label: 'Disciplinary Log', // Coming next
|
|
90
|
+
action: 'hr-disciplinary',
|
|
91
|
+
roles: ['owner'],
|
|
63
92
|
},
|
|
64
93
|
]
|
|
65
94
|
},
|
|
66
|
-
/*
|
|
67
|
-
{
|
|
68
|
-
type: 'link',
|
|
69
|
-
label: 'Business Intelligence',
|
|
70
|
-
icon: 'bi bi-graph-up-arrow',
|
|
71
|
-
href: '/app/analytics'
|
|
72
|
-
},
|
|
73
|
-
*/
|
|
95
|
+
/* ── end HR MODULE ── */
|
|
74
96
|
{
|
|
75
97
|
type: 'button',
|
|
76
98
|
label: 'Maintenance',
|
|
@@ -84,26 +106,20 @@ export const defaultMenuItems = [
|
|
|
84
106
|
icon: 'bi bi-people',
|
|
85
107
|
roles: ['owner', 'staff', 'captain'],
|
|
86
108
|
children: [
|
|
87
|
-
{
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
roles: ['owner', 'staff']
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
type: 'separator'
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
label: 'Get Crew by Vessel',
|
|
97
|
-
action: 'crew-by-vessel'
|
|
98
|
-
}
|
|
109
|
+
{ label: 'All Crew', action: 'crew-all', roles: ['owner', 'staff'] },
|
|
110
|
+
{ label: 'Get Crew by Vessel', action: 'crew-by-vessel' },
|
|
111
|
+
{ label: 'Schedule', action: 'schedule' },
|
|
99
112
|
]
|
|
100
113
|
},
|
|
101
114
|
{
|
|
102
|
-
type: '
|
|
115
|
+
type: 'dropdown',
|
|
103
116
|
label: 'Inventory Management',
|
|
104
117
|
icon: 'bi bi-clipboard-data',
|
|
105
|
-
|
|
106
|
-
|
|
118
|
+
roles: ['owner', 'staff', 'captain'],
|
|
119
|
+
children: [
|
|
120
|
+
{ label: 'Parts Inventory', action: 'inventory', roles: ['owner', 'staff', 'captain'] },
|
|
121
|
+
{ label: 'Bunkering', action: 'fuel', roles: ['owner', 'staff', 'captain'] },
|
|
122
|
+
]
|
|
107
123
|
},
|
|
108
124
|
{
|
|
109
125
|
type: 'link',
|
|
@@ -118,36 +134,34 @@ export const defaultMenuItems = [
|
|
|
118
134
|
icon: 'fas fa-ship',
|
|
119
135
|
action: 'coming-soon',
|
|
120
136
|
roles: ['owner', 'staff', 'captain']
|
|
121
|
-
|
|
122
137
|
},
|
|
123
138
|
{
|
|
124
139
|
type: 'button',
|
|
125
140
|
label: 'Vessel Log',
|
|
126
141
|
icon: 'bi bi-file-ruled',
|
|
127
|
-
action: '
|
|
142
|
+
action: 'coming-soon',
|
|
128
143
|
roles: ['owner', 'staff', 'captain']
|
|
129
144
|
},
|
|
130
|
-
{
|
|
131
|
-
type: 'button',
|
|
132
|
-
label: 'Settings',
|
|
133
|
-
icon: 'bi bi-gear',
|
|
134
|
-
action: 'settings',
|
|
135
|
-
roles: ['owner', 'staff']
|
|
136
|
-
},
|
|
137
145
|
{
|
|
138
146
|
type: 'button',
|
|
139
147
|
label: 'Profile',
|
|
140
148
|
icon: 'bi bi-people',
|
|
141
149
|
action: 'crew-profile',
|
|
142
|
-
|
|
143
|
-
|
|
150
|
+
roles: ['crew']
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
type: 'button',
|
|
154
|
+
label: 'Training',
|
|
155
|
+
icon: 'bi bi-book',
|
|
156
|
+
action: 'crew-training',
|
|
157
|
+
roles: ['crew']
|
|
144
158
|
},
|
|
145
159
|
{
|
|
146
160
|
type: 'button',
|
|
147
|
-
label: '
|
|
148
|
-
icon: 'bi bi-
|
|
149
|
-
action: '
|
|
150
|
-
|
|
161
|
+
label: 'Settings',
|
|
162
|
+
icon: 'bi bi-gear',
|
|
163
|
+
action: 'settings',
|
|
164
|
+
roles: ['owner', 'staff']
|
|
151
165
|
},
|
|
152
166
|
{
|
|
153
167
|
type: 'button',
|