@iservice365/layer-common 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/components/CameraForm.vue +264 -0
- package/components/CameraMain.vue +352 -0
- package/components/Card/DeleteConfirmation.vue +51 -0
- package/components/Card/MemberInfoSummary.vue +44 -0
- package/components/Chat/Information.vue +28 -11
- package/components/Dialog/DeleteConfirmation.vue +51 -0
- package/components/Dialog/UpdateMoreAction.vue +99 -0
- package/components/Feedback/Form.vue +17 -3
- package/components/FeedbackDetail.vue +0 -11
- package/components/FeedbackMain.vue +21 -11
- package/components/Input/DateTimePicker.vue +10 -5
- package/components/Input/File.vue +1 -1
- package/components/Input/FileV2.vue +106 -63
- package/components/Input/InputPhoneNumberV2.vue +114 -0
- package/components/Input/NRICNumber.vue +41 -0
- package/components/Input/PhoneNumber.vue +1 -0
- package/components/Input/VehicleNumber.vue +49 -0
- package/components/NumberSettingField.vue +107 -0
- package/components/PeopleForm.vue +452 -0
- package/components/TableMain.vue +2 -1
- package/components/VehicleUpdateMoreAction.vue +84 -0
- package/components/VisitorForm.vue +712 -0
- package/components/VisitorFormSelection.vue +53 -0
- package/components/VisitorManagement.vue +569 -0
- package/components/WorkOrder/Create.vue +87 -49
- package/components/WorkOrder/Main.vue +17 -12
- package/composables/useBuilding.ts +250 -0
- package/composables/useBuildingUnit.ts +116 -0
- package/composables/useFeedback.ts +3 -3
- package/composables/useFile.ts +7 -9
- package/composables/useLocal.ts +67 -0
- package/composables/usePeople.ts +48 -0
- package/composables/useSecurityUtils.ts +18 -0
- package/composables/useSiteSettings.ts +111 -0
- package/composables/useUtils.ts +30 -1
- package/composables/useVisitor.ts +80 -0
- package/composables/useWorkOrder.ts +3 -3
- package/package.json +1 -1
- package/plugins/vuetify.ts +6 -1
- package/types/building.d.ts +19 -0
- package/types/camera.d.ts +31 -0
- package/types/people.d.ts +22 -0
- package/types/select.d.ts +4 -0
- package/types/site.d.ts +10 -7
- package/types/visitor.d.ts +42 -0
- package/utils/phoneMasks.ts +1703 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-row no-gutters class="mb-5">
|
|
3
|
+
<v-col cols="12" class="d-flex ga-2">
|
|
4
|
+
<v-select v-model="selectedCode" :variant="variant" :items="countries" item-title="code" item-value="code"
|
|
5
|
+
hide-details class="px-0" :density="density" style="max-width: 95px" :rules="[...props.rules]"
|
|
6
|
+
@update:model-value="handleUpdateCountry">
|
|
7
|
+
<template v-slot:item="{ props: itemProps, item }">
|
|
8
|
+
<v-list-item v-bind="itemProps" :title="item.raw.name" :subtitle="item.raw.dial_code" width="300" />
|
|
9
|
+
</template>
|
|
10
|
+
</v-select>
|
|
11
|
+
<v-mask-input v-model="phone" :mask="currentMask" :rules="[...props.rules, validatePhone]"
|
|
12
|
+
:variant="variant" hint="Enter a valid phone number" hide-details persistent-hint return-masked-value
|
|
13
|
+
:density="density" :placeholder="placeholder || currentMask"></v-mask-input>
|
|
14
|
+
</v-col>
|
|
15
|
+
<span class="text-error text-caption w-100" v-if="errorMessage && !hideDetails">{{ errorMessage }}</span>
|
|
16
|
+
</v-row>
|
|
17
|
+
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script setup lang="ts">
|
|
21
|
+
import { ref, computed, type PropType } from 'vue'
|
|
22
|
+
import type { ValidationRule } from 'vuetify/lib/types.mjs'
|
|
23
|
+
import phoneMasks from '~/utils/phoneMasks'
|
|
24
|
+
|
|
25
|
+
const props = defineProps({
|
|
26
|
+
rules: {
|
|
27
|
+
type: Array as PropType<ValidationRule[]>,
|
|
28
|
+
default: []
|
|
29
|
+
},
|
|
30
|
+
variant: {
|
|
31
|
+
type: String as PropType<any>,
|
|
32
|
+
default: "outlined"
|
|
33
|
+
},
|
|
34
|
+
density: {
|
|
35
|
+
type: String as PropType<Density>,
|
|
36
|
+
default: "default"
|
|
37
|
+
},
|
|
38
|
+
placeholder: {
|
|
39
|
+
type: String,
|
|
40
|
+
},
|
|
41
|
+
hideDetails: {
|
|
42
|
+
type: Boolean,
|
|
43
|
+
default: false
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
type Density = 'default' | 'comfortable' | 'compact';
|
|
48
|
+
|
|
49
|
+
type TPhoneMask =
|
|
50
|
+
{
|
|
51
|
+
name: string
|
|
52
|
+
flag: string
|
|
53
|
+
code: string;
|
|
54
|
+
dial_code: string;
|
|
55
|
+
regex: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const phone = defineModel({ default: '' })
|
|
59
|
+
const selectedCode = ref('SG')
|
|
60
|
+
const countries = phoneMasks
|
|
61
|
+
const errorMessage = ref('')
|
|
62
|
+
|
|
63
|
+
const currentMask = computed(() => {
|
|
64
|
+
const country = phoneMasks.find((c: TPhoneMask) => c.code === selectedCode.value)
|
|
65
|
+
if (!country) return '############'
|
|
66
|
+
return generateMaskFromRegex(country.regex)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
const validatePhone = (value: string): boolean | string => {
|
|
72
|
+
if (!value) {
|
|
73
|
+
errorMessage.value = ''
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
const country = phoneMasks.find((c: any) => c.code === selectedCode.value)
|
|
77
|
+
if (!country) {
|
|
78
|
+
errorMessage.value = ''
|
|
79
|
+
return true
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const regex = new RegExp(country.regex)
|
|
83
|
+
const isValid = regex.test(value)
|
|
84
|
+
|
|
85
|
+
errorMessage.value = isValid ? '' : `Invalid ${country.name} phone number`
|
|
86
|
+
return isValid
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
function generateMaskFromRegex(regex: string): string {
|
|
93
|
+
|
|
94
|
+
let pattern = regex.replace(/^\^|\$$/g, '')
|
|
95
|
+
pattern = pattern.replace(/\\d\{(\d+)\}/g, (_, count) => '#'.repeat(Number(count)))
|
|
96
|
+
pattern = pattern.replace(/\\d/g, '#')
|
|
97
|
+
|
|
98
|
+
pattern = pattern.replace(/\\/g, '')
|
|
99
|
+
|
|
100
|
+
pattern = pattern.replace(/\s\?/g, ' ')
|
|
101
|
+
pattern = pattern.replace(/\s/g, ' ')
|
|
102
|
+
|
|
103
|
+
pattern = pattern.replace(/\(\?:/g, '(')
|
|
104
|
+
|
|
105
|
+
return pattern.trim()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function handleUpdateCountry() {
|
|
109
|
+
phone.value = ''
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
</script>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-text-field
|
|
3
|
+
v-model.trim="model"
|
|
4
|
+
:rules="rules"
|
|
5
|
+
:maxlength="maxlength"
|
|
6
|
+
:placeholder="placeholder"
|
|
7
|
+
:counter="maxlength"
|
|
8
|
+
@input="onInput"
|
|
9
|
+
outlined
|
|
10
|
+
clearable
|
|
11
|
+
/>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup>
|
|
15
|
+
|
|
16
|
+
const props = defineProps({
|
|
17
|
+
placeholder: {
|
|
18
|
+
type: String,
|
|
19
|
+
default: ''
|
|
20
|
+
},
|
|
21
|
+
rules: {
|
|
22
|
+
type: Array,
|
|
23
|
+
default: () => []
|
|
24
|
+
},
|
|
25
|
+
maxlength: {
|
|
26
|
+
type: [Number, String],
|
|
27
|
+
default: false
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const model = defineModel({required: true, default: ""})
|
|
32
|
+
|
|
33
|
+
function onInput(event) {
|
|
34
|
+
const value = typeof event === 'string' ? event : event?.target?.value || ''
|
|
35
|
+
|
|
36
|
+
let formatted = value.replace(/[^A-Za-z0-9]/g, '')
|
|
37
|
+
formatted = formatted.toUpperCase()
|
|
38
|
+
|
|
39
|
+
model.value = formatted
|
|
40
|
+
}
|
|
41
|
+
</script>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-text-field
|
|
3
|
+
v-model="model"
|
|
4
|
+
:rules="rules"
|
|
5
|
+
:maxlength="maxlength"
|
|
6
|
+
:placeholder="placeholder"
|
|
7
|
+
:counter="maxlength"
|
|
8
|
+
@input="onInput"
|
|
9
|
+
outlined
|
|
10
|
+
:disabled="disabled"
|
|
11
|
+
:readonly="readonly"
|
|
12
|
+
clearable
|
|
13
|
+
/>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup>
|
|
17
|
+
|
|
18
|
+
const props = defineProps({
|
|
19
|
+
placeholder: {
|
|
20
|
+
type: String,
|
|
21
|
+
default: 'Vehicle Number'
|
|
22
|
+
},
|
|
23
|
+
rules: {
|
|
24
|
+
type: Array,
|
|
25
|
+
default: () => []
|
|
26
|
+
},
|
|
27
|
+
maxlength: {
|
|
28
|
+
type: [Number, String],
|
|
29
|
+
default: false
|
|
30
|
+
},
|
|
31
|
+
disabled: {
|
|
32
|
+
type: Boolean,
|
|
33
|
+
},
|
|
34
|
+
readonly: {
|
|
35
|
+
type: Boolean,
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const model = defineModel({required: true, default: ""})
|
|
40
|
+
|
|
41
|
+
function onInput(event) {
|
|
42
|
+
const value = typeof event === 'string' ? event : event?.target?.value || ''
|
|
43
|
+
|
|
44
|
+
let formatted = value.replace(/[^A-Za-z0-9]/g, '')
|
|
45
|
+
formatted = formatted.toUpperCase()
|
|
46
|
+
|
|
47
|
+
model.value = formatted
|
|
48
|
+
}
|
|
49
|
+
</script>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-row no-gutters>
|
|
3
|
+
<v-form v-model="valid">
|
|
4
|
+
<v-row>
|
|
5
|
+
<v-col cols="6">
|
|
6
|
+
<InputLabel class="text-capitalize font-weight-bold" :title="title" :required="required" />
|
|
7
|
+
<v-text-field v-model.number="count" type="number" density="comfortable" :rules="rules" />
|
|
8
|
+
</v-col>
|
|
9
|
+
|
|
10
|
+
<v-col cols="6">
|
|
11
|
+
<v-btn color="primary" class="text-none mt-6" size="large" variant="flat"
|
|
12
|
+
:disabled="!valid || disabled" :loading="loading || updating" text="Save" @click="handleSave" />
|
|
13
|
+
</v-col>
|
|
14
|
+
</v-row>
|
|
15
|
+
</v-form>
|
|
16
|
+
<Snackbar v-model="toastObject.show" :text="toastObject.message" :color="toastObject.color" />
|
|
17
|
+
</v-row>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script setup lang="ts">
|
|
21
|
+
import useSiteSettings from '../composables/useSiteSettings';
|
|
22
|
+
|
|
23
|
+
const props = defineProps({
|
|
24
|
+
title: { type: String, required: true },
|
|
25
|
+
required: { type: Boolean, default: true },
|
|
26
|
+
loading: { type: Boolean, default: false },
|
|
27
|
+
type: { type: String as PropType<'blocks' | 'guard_posts'>, required: true},
|
|
28
|
+
disabled: {type: Boolean, default: false},
|
|
29
|
+
siteId: { type: String, required: true }
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const { updateSite, setSiteGuardPosts } = useSiteSettings()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
const count = defineModel<number>({ default: 0 });
|
|
36
|
+
const valid = ref(false);
|
|
37
|
+
const updating = ref(false)
|
|
38
|
+
|
|
39
|
+
const toastObject = reactive({
|
|
40
|
+
show: false,
|
|
41
|
+
message: "",
|
|
42
|
+
color: "",
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
const emits = defineEmits(["success", "error"]);
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
const { requiredRule } = useUtils();
|
|
50
|
+
|
|
51
|
+
function validNumber(value: number) {
|
|
52
|
+
return value > 0 || "Number must be greater than zero.";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const rules = computed(() => {
|
|
56
|
+
const arr = [];
|
|
57
|
+
if (props.required) {
|
|
58
|
+
arr.push(requiredRule);
|
|
59
|
+
arr.push(validNumber);
|
|
60
|
+
}
|
|
61
|
+
return arr;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
async function handleSave() {
|
|
68
|
+
if(!props.siteId){
|
|
69
|
+
return emits('error', 'Site ID is required')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
updating.value = true;
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
if(props.type === 'blocks'){
|
|
76
|
+
await updateSite(props.siteId, { block: count.value});
|
|
77
|
+
handleSuccess('The number of blocks has been updated successfully!')
|
|
78
|
+
} else if (props.type) {
|
|
79
|
+
await setSiteGuardPosts(props.siteId, count.value)
|
|
80
|
+
handleSuccess('The number of guard posts has been updated successfully!')
|
|
81
|
+
|
|
82
|
+
} else {
|
|
83
|
+
throw new Error('Invalid type Prop provided.')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
emits('success')
|
|
87
|
+
} catch (error: any) {
|
|
88
|
+
const message = error?.data?.message || `Error updating ${props.type.replace('_', ' ')}`;
|
|
89
|
+
handleError(message)
|
|
90
|
+
} finally {
|
|
91
|
+
updating.value = false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const handleSuccess = (message: string) => {
|
|
96
|
+
toastObject.show = true;
|
|
97
|
+
toastObject.message = message;
|
|
98
|
+
toastObject.color = "success";
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const handleError = (message: string) => {
|
|
102
|
+
toastObject.show = true;
|
|
103
|
+
toastObject.message = message;
|
|
104
|
+
toastObject.color = "error";
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
</script>
|