@iservice365/layer-common 0.0.6 → 0.2.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/.playground/app.vue +7 -2
- package/.playground/pages/feedback.vue +30 -0
- package/CHANGELOG.md +12 -0
- package/components/Chat/Bubbles.vue +53 -0
- package/components/Chat/Information.vue +187 -0
- package/components/Chat/ListCard.vue +62 -0
- package/components/Chat/Message.vue +149 -0
- package/components/Chat/Navigation.vue +150 -0
- package/components/ConfirmDialog.vue +66 -0
- package/components/Container/Standard.vue +33 -0
- package/components/Feedback/Form.vue +136 -0
- package/components/FeedbackDetail.vue +465 -0
- package/components/FeedbackMain.vue +454 -0
- package/components/FormDialog.vue +65 -0
- package/components/Input/File.vue +203 -0
- package/components/Input/ListGroupSelection.vue +96 -0
- package/components/Input/NewDate.vue +123 -0
- package/components/Input/Number.vue +124 -0
- package/components/InvitationMain.vue +284 -0
- package/components/Layout/Header.vue +28 -4
- package/components/ListView.vue +87 -0
- package/components/MemberMain.vue +459 -0
- package/components/RolePermissionFormCreate.vue +161 -0
- package/components/RolePermissionFormPreviewUpdate.vue +183 -0
- package/components/RolePermissionMain.vue +361 -0
- package/components/ServiceProviderFormCreate.vue +154 -0
- package/components/ServiceProviderMain.vue +195 -0
- package/components/SignaturePad.vue +73 -0
- package/components/SpecificAttr.vue +53 -0
- package/components/SwitchContext.vue +26 -5
- package/components/TableList.vue +150 -0
- package/components/TableListSecondary.vue +164 -0
- package/components/WorkOrder/Create.vue +197 -0
- package/components/WorkOrder/ListView.vue +96 -0
- package/components/WorkOrder/Main.vue +308 -0
- package/components/Workorder.vue +1 -0
- package/composables/useAddress.ts +107 -0
- package/composables/useCommonPermission.ts +130 -0
- package/composables/useCustomer.ts +113 -0
- package/composables/useFeedback.ts +117 -0
- package/composables/useFile.ts +40 -0
- package/composables/useInvoice.ts +18 -0
- package/composables/useLocal.ts +24 -4
- package/composables/useLocalAuth.ts +58 -14
- package/composables/useLocalSetup.ts +52 -0
- package/composables/useMember.ts +104 -0
- package/composables/useOrg.ts +76 -92
- package/composables/usePaymentMethod.ts +101 -0
- package/composables/usePrice.ts +15 -0
- package/composables/usePromoCode.ts +36 -0
- package/composables/useRole.ts +38 -7
- package/composables/useServiceProvider.ts +218 -0
- package/composables/useSite.ts +108 -0
- package/composables/useSubscription.ts +149 -0
- package/composables/useUser.ts +38 -14
- package/composables/useUtils.ts +218 -6
- package/composables/useVerification.ts +33 -0
- package/composables/useWorkOrder.ts +68 -0
- package/middleware/01.auth.ts +11 -0
- package/middleware/02.org.ts +18 -0
- package/middleware/03.customer.ts +13 -0
- package/nuxt.config.ts +2 -1
- package/package.json +7 -3
- package/pages/index.vue +3 -0
- package/pages/payment-method-linked.vue +31 -0
- package/pages/require-customer.vue +56 -0
- package/pages/require-organization-membership.vue +47 -0
- package/pages/unauthorized.vue +29 -0
- package/plugins/API.ts +1 -3
- package/plugins/iconify.client.ts +5 -0
- package/plugins/vuetify.ts +2 -0
- package/public/bg-camera.jpg +0 -0
- package/public/bg-city.jpg +0 -0
- package/public/bg-condo.jpg +0 -0
- package/public/images/icons/delete-icon.png +0 -0
- package/public/sprite.svg +1 -0
- package/types/address.d.ts +13 -0
- package/types/customer.d.ts +15 -0
- package/types/feedback.d.ts +63 -0
- package/types/local.d.ts +46 -38
- package/types/member.d.ts +21 -0
- package/types/org.d.ts +13 -0
- package/types/permission.d.ts +1 -0
- package/types/price.d.ts +17 -0
- package/types/promo-code.d.ts +19 -0
- package/types/service-provider.d.ts +15 -0
- package/types/site.d.ts +13 -0
- package/types/subscription.d.ts +23 -0
- package/types/user.d.ts +19 -0
- package/types/verification.d.ts +20 -0
- package/types/work-order.d.ts +40 -0
package/.playground/app.vue
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
<v-app>
|
|
3
3
|
<LayoutHeader />
|
|
4
4
|
|
|
5
|
-
<LayoutNavigationDrawer :navigation-items="navigationItems"/>
|
|
5
|
+
<LayoutNavigationDrawer :navigation-items="navigationItems" />
|
|
6
6
|
|
|
7
7
|
<v-main>
|
|
8
|
-
<
|
|
8
|
+
<NuxtLayout> <NuxtPage /> </NuxtLayout>
|
|
9
9
|
</v-main>
|
|
10
10
|
</v-app>
|
|
11
11
|
</template>
|
|
@@ -28,6 +28,11 @@ const navigationItems = computed(() => {
|
|
|
28
28
|
title: "Draft",
|
|
29
29
|
route: { name: "index" },
|
|
30
30
|
icon: "mdi-account-circle",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
title: "Feedback",
|
|
34
|
+
route: { name: "feedback" },
|
|
35
|
+
icon: "mdi-account-circle",
|
|
31
36
|
}
|
|
32
37
|
);
|
|
33
38
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-row class="no-gutters" align="center" justify="center">
|
|
3
|
+
<v-col cols="12" md="10" lg="10">
|
|
4
|
+
<FeedbackListView @click:create="showCreateDialog = true" />
|
|
5
|
+
</v-col>
|
|
6
|
+
|
|
7
|
+
<v-dialog
|
|
8
|
+
v-model="showCreateDialog"
|
|
9
|
+
fullscreen
|
|
10
|
+
transition="dialog-right-transition"
|
|
11
|
+
persistent
|
|
12
|
+
>
|
|
13
|
+
<v-card
|
|
14
|
+
class="ml-auto"
|
|
15
|
+
style="
|
|
16
|
+
height: 100vh;
|
|
17
|
+
width: 400px;
|
|
18
|
+
border-top-left-radius: 12px;
|
|
19
|
+
border-bottom-left-radius: 12px;
|
|
20
|
+
"
|
|
21
|
+
>
|
|
22
|
+
<FeedbackFormCreate @close="showCreateDialog = false" />
|
|
23
|
+
</v-card>
|
|
24
|
+
</v-dialog>
|
|
25
|
+
</v-row>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script lang="ts" setup>
|
|
29
|
+
const showCreateDialog = ref(false);
|
|
30
|
+
</script>
|
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card class="mb-2 pa-4 rounded-lg" width="400" elevation="2">
|
|
3
|
+
<v-row no-gutters align="center">
|
|
4
|
+
<v-col cols="auto" class="mr-3">
|
|
5
|
+
<v-avatar color="surface-variant" size="35">
|
|
6
|
+
<v-img v-if="currentUser?.profile" width="15" height="15" />
|
|
7
|
+
<span v-else class="text-h5">{{ getNameInitials(senderName) }}</span>
|
|
8
|
+
</v-avatar>
|
|
9
|
+
</v-col>
|
|
10
|
+
|
|
11
|
+
<v-col class="d-flex flex-column">
|
|
12
|
+
<span class="text-body-2 font-weight-medium">{{ senderName }}</span>
|
|
13
|
+
<span class="text-caption text-grey">{{ role }}</span>
|
|
14
|
+
</v-col>
|
|
15
|
+
</v-row>
|
|
16
|
+
|
|
17
|
+
<v-row no-gutters class="mt-1">
|
|
18
|
+
<v-col>
|
|
19
|
+
<span class="py-2">{{ message }}</span>
|
|
20
|
+
|
|
21
|
+
<v-row no-gutters justify="space-between" align="center" class="mt-1">
|
|
22
|
+
<v-col cols="auto">
|
|
23
|
+
<span class="text-caption">{{ timestamp }}</span>
|
|
24
|
+
</v-col>
|
|
25
|
+
<v-col
|
|
26
|
+
v-if="isCurrentUser"
|
|
27
|
+
cols="auto"
|
|
28
|
+
class="d-flex align-center bg-grey-200"
|
|
29
|
+
>
|
|
30
|
+
<v-icon size="16" class="mr-1 text--secondary"
|
|
31
|
+
>mdi-eye-outline</v-icon
|
|
32
|
+
>
|
|
33
|
+
<span class="text-caption text--secondary">{{ seenLabel }}</span>
|
|
34
|
+
</v-col>
|
|
35
|
+
</v-row>
|
|
36
|
+
</v-col>
|
|
37
|
+
</v-row>
|
|
38
|
+
</v-card>
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<script setup lang="ts">
|
|
42
|
+
defineProps<{
|
|
43
|
+
isCurrentUser: boolean;
|
|
44
|
+
message: string;
|
|
45
|
+
timestamp: string;
|
|
46
|
+
seenLabel?: string;
|
|
47
|
+
senderName: string;
|
|
48
|
+
role?: string;
|
|
49
|
+
}>();
|
|
50
|
+
|
|
51
|
+
const { currentUser } = useLocalAuth();
|
|
52
|
+
const { getNameInitials } = useUtils();
|
|
53
|
+
</script>
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-sheet
|
|
3
|
+
elevation="1"
|
|
4
|
+
rounded="0"
|
|
5
|
+
class="fill-height d-flex flex-column"
|
|
6
|
+
style="border-right: 1px solid #e0e0e0; border-top: 1px solid #e0e0e0"
|
|
7
|
+
>
|
|
8
|
+
<v-row class="pa-4">
|
|
9
|
+
<v-sheet
|
|
10
|
+
class="overflow-y-auto flex-grow-1"
|
|
11
|
+
:style="{ height: 'calc(100vh - 180px)', overflowX: 'hidden' }"
|
|
12
|
+
>
|
|
13
|
+
<v-col
|
|
14
|
+
cols="12"
|
|
15
|
+
class="mb-2"
|
|
16
|
+
style="position: sticky; top: 0; background-color: white; z-index: 1"
|
|
17
|
+
>
|
|
18
|
+
<h3 class="text-h6">Information</h3>
|
|
19
|
+
</v-col>
|
|
20
|
+
|
|
21
|
+
<v-col cols="12" class="text-center mb-3" v-if="item">
|
|
22
|
+
<v-avatar
|
|
23
|
+
size="48"
|
|
24
|
+
class="mb-1"
|
|
25
|
+
v-if="!item.attachments || !item.attachments.length"
|
|
26
|
+
>
|
|
27
|
+
<v-img v-if="currentUser?.profile" width="15" height="15" />
|
|
28
|
+
<span v-else class="text-h5">{{
|
|
29
|
+
getNameInitials(senderName)
|
|
30
|
+
}}</span>
|
|
31
|
+
</v-avatar>
|
|
32
|
+
|
|
33
|
+
<v-img
|
|
34
|
+
v-else-if="item.attachments.length === 1"
|
|
35
|
+
:src="item.attachments[0]"
|
|
36
|
+
height="200"
|
|
37
|
+
cover
|
|
38
|
+
class="rounded mb-2"
|
|
39
|
+
/>
|
|
40
|
+
|
|
41
|
+
<v-carousel
|
|
42
|
+
v-else
|
|
43
|
+
hide-delimiter-background
|
|
44
|
+
height="200"
|
|
45
|
+
class="rounded mb-2"
|
|
46
|
+
show-arrows
|
|
47
|
+
cycle
|
|
48
|
+
>
|
|
49
|
+
<v-carousel-item
|
|
50
|
+
v-for="(attachment, index) in item.attachments"
|
|
51
|
+
:key="index"
|
|
52
|
+
>
|
|
53
|
+
<v-img :src="attachment" cover class="w-100 h-100" />
|
|
54
|
+
</v-carousel-item>
|
|
55
|
+
</v-carousel>
|
|
56
|
+
</v-col>
|
|
57
|
+
|
|
58
|
+
<v-col cols="12" v-if="item">
|
|
59
|
+
<v-row dense class="my-1">
|
|
60
|
+
<v-col cols="6" class="py-1"><strong>Category:</strong></v-col>
|
|
61
|
+
<v-col cols="6" class="py-1 text-right text-capitalize">{{ item.category }}</v-col>
|
|
62
|
+
</v-row>
|
|
63
|
+
|
|
64
|
+
<v-divider />
|
|
65
|
+
|
|
66
|
+
<v-row dense class="my-1">
|
|
67
|
+
<v-col cols="6" class="py-1"><strong>Location:</strong></v-col>
|
|
68
|
+
<v-col cols="6" class="py-1 text-right">{{ item.location }}</v-col>
|
|
69
|
+
</v-row>
|
|
70
|
+
<v-divider />
|
|
71
|
+
|
|
72
|
+
<v-row dense class="my-1">
|
|
73
|
+
<v-col cols="12" class="py-1"
|
|
74
|
+
><strong>Description Feedback:</strong></v-col
|
|
75
|
+
>
|
|
76
|
+
<v-col cols="12" class="py-1">
|
|
77
|
+
{{ item.description }}
|
|
78
|
+
</v-col>
|
|
79
|
+
</v-row>
|
|
80
|
+
<v-divider />
|
|
81
|
+
|
|
82
|
+
<v-row dense class="my-1">
|
|
83
|
+
<v-col cols="12" class="py-1"
|
|
84
|
+
><strong>Description Work Order:</strong></v-col
|
|
85
|
+
>
|
|
86
|
+
<v-col cols="12" class="py-1">
|
|
87
|
+
{{
|
|
88
|
+
item.workOrderDescription ||
|
|
89
|
+
"No work order description available"
|
|
90
|
+
}}
|
|
91
|
+
</v-col>
|
|
92
|
+
</v-row>
|
|
93
|
+
<v-divider />
|
|
94
|
+
|
|
95
|
+
<v-row dense class="my-1">
|
|
96
|
+
<v-col cols="6" class="py-1"><strong>Workorder:</strong></v-col>
|
|
97
|
+
<v-col cols="6" class="py-1 text-right">{{ item.workOrder }}</v-col>
|
|
98
|
+
</v-row>
|
|
99
|
+
|
|
100
|
+
<v-divider />
|
|
101
|
+
|
|
102
|
+
<v-row dense class="my-1">
|
|
103
|
+
<v-col cols="6" class="py-1"><strong>Status:</strong></v-col>
|
|
104
|
+
<v-col cols="6" class="py-1 text-right">
|
|
105
|
+
<v-chip
|
|
106
|
+
:color="getColorStatus(item.status)"
|
|
107
|
+
size="x-small"
|
|
108
|
+
text-color="white"
|
|
109
|
+
>
|
|
110
|
+
{{ item.status }}
|
|
111
|
+
</v-chip>
|
|
112
|
+
</v-col>
|
|
113
|
+
</v-row>
|
|
114
|
+
|
|
115
|
+
<v-divider />
|
|
116
|
+
|
|
117
|
+
<v-row dense class="my-1">
|
|
118
|
+
<v-col cols="6" class="py-1"><strong>Created At:</strong></v-col>
|
|
119
|
+
<v-col cols="6" class="py-1 text-right">{{
|
|
120
|
+
formatDate(item.createdAt)
|
|
121
|
+
}}</v-col>
|
|
122
|
+
</v-row>
|
|
123
|
+
</v-col>
|
|
124
|
+
|
|
125
|
+
<v-col cols="12">
|
|
126
|
+
<v-btn
|
|
127
|
+
block
|
|
128
|
+
color="primary"
|
|
129
|
+
variant="flat"
|
|
130
|
+
class="mb-2 text-capitalize"
|
|
131
|
+
@click="$emit('edit')"
|
|
132
|
+
>
|
|
133
|
+
Edit
|
|
134
|
+
</v-btn>
|
|
135
|
+
</v-col>
|
|
136
|
+
</v-sheet>
|
|
137
|
+
</v-row>
|
|
138
|
+
|
|
139
|
+
<v-sheet class="px-4 py-2">
|
|
140
|
+
<v-btn
|
|
141
|
+
v-if="item?.status !== 'Completed'"
|
|
142
|
+
block
|
|
143
|
+
color="primary-button"
|
|
144
|
+
density="default"
|
|
145
|
+
class="mb-2 text-capitalize"
|
|
146
|
+
@click="$emit('mark-complete-request')"
|
|
147
|
+
>
|
|
148
|
+
Mark as Complete
|
|
149
|
+
</v-btn>
|
|
150
|
+
<v-btn
|
|
151
|
+
block
|
|
152
|
+
variant="flat"
|
|
153
|
+
color="red"
|
|
154
|
+
density="default"
|
|
155
|
+
class="text-white text-capitalize"
|
|
156
|
+
@click="$emit('delete')"
|
|
157
|
+
>
|
|
158
|
+
Delete
|
|
159
|
+
</v-btn>
|
|
160
|
+
</v-sheet>
|
|
161
|
+
</v-sheet>
|
|
162
|
+
</template>
|
|
163
|
+
|
|
164
|
+
<script setup lang="ts">
|
|
165
|
+
const { getColorStatus, formatDate } = useUtils();
|
|
166
|
+
|
|
167
|
+
defineProps({
|
|
168
|
+
item: {
|
|
169
|
+
type: Object,
|
|
170
|
+
default: () => ({}),
|
|
171
|
+
},
|
|
172
|
+
senderName: {
|
|
173
|
+
type: String,
|
|
174
|
+
default: "",
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const emit = defineEmits([
|
|
179
|
+
"update:provider",
|
|
180
|
+
"mark-complete-request",
|
|
181
|
+
"delete",
|
|
182
|
+
"edit",
|
|
183
|
+
]);
|
|
184
|
+
|
|
185
|
+
const { currentUser } = useLocalAuth();
|
|
186
|
+
const { getNameInitials } = useUtils();
|
|
187
|
+
</script>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card class="mb-2 pa-2 rounded-lg border-sm" elevation="0">
|
|
3
|
+
<v-row no-gutters align="center">
|
|
4
|
+
<v-col cols="12" sm="4" class="d-flex justify-center align-center">
|
|
5
|
+
<v-img
|
|
6
|
+
:src="image"
|
|
7
|
+
alt="Photo"
|
|
8
|
+
max-height="100"
|
|
9
|
+
width="100%"
|
|
10
|
+
cover
|
|
11
|
+
class="rounded-lg"
|
|
12
|
+
/>
|
|
13
|
+
</v-col>
|
|
14
|
+
|
|
15
|
+
<v-col cols="12" sm="8" class="py-2 pe-1">
|
|
16
|
+
<v-row dense>
|
|
17
|
+
<v-col cols="12" class="d-flex align-center flex-wrap">
|
|
18
|
+
<span class="text-caption font-weight-medium me-1">Created by</span>
|
|
19
|
+
|
|
20
|
+
<v-avatar color="surface-variant" size="20" class="me-1">
|
|
21
|
+
<v-img
|
|
22
|
+
v-if="userProfile"
|
|
23
|
+
:src="userProfile"
|
|
24
|
+
width="20"
|
|
25
|
+
height="20"
|
|
26
|
+
/>
|
|
27
|
+
<span v-else class="text-overline">
|
|
28
|
+
{{ getNameInitials }}
|
|
29
|
+
</span>
|
|
30
|
+
</v-avatar>
|
|
31
|
+
|
|
32
|
+
<span class="text-caption">{{ createdByName || "Unknown" }}</span>
|
|
33
|
+
</v-col>
|
|
34
|
+
|
|
35
|
+
<v-col cols="12" class="text-caption">Subject: {{ subject }}</v-col>
|
|
36
|
+
|
|
37
|
+
<v-col cols="12" class="text-caption">
|
|
38
|
+
Status: <span class="text-orange">{{ status }}</span>
|
|
39
|
+
</v-col>
|
|
40
|
+
|
|
41
|
+
<v-col cols="12" class="text-caption">
|
|
42
|
+
Date Created: {{ createdAt }}
|
|
43
|
+
</v-col>
|
|
44
|
+
</v-row>
|
|
45
|
+
</v-col>
|
|
46
|
+
</v-row>
|
|
47
|
+
</v-card>
|
|
48
|
+
</template>
|
|
49
|
+
|
|
50
|
+
<script setup lang="ts">
|
|
51
|
+
const { getNameInitials } = useUtils();
|
|
52
|
+
|
|
53
|
+
defineProps({
|
|
54
|
+
image: String,
|
|
55
|
+
subject: String,
|
|
56
|
+
status: String,
|
|
57
|
+
createdAt: String,
|
|
58
|
+
createdByName: String,
|
|
59
|
+
userProfile: String,
|
|
60
|
+
getNameInitials: Function,
|
|
61
|
+
});
|
|
62
|
+
</script>
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-sheet
|
|
3
|
+
elevation="1"
|
|
4
|
+
rounded="0"
|
|
5
|
+
class="fill-height d-flex flex-column"
|
|
6
|
+
style="border-right: 1px solid #e0e0e0; border-top: 1px solid #e0e0e0"
|
|
7
|
+
>
|
|
8
|
+
<div style="border-bottom: 1px solid #e0e0e0" class="px-4 pt-4 pb-2 mb-2">
|
|
9
|
+
<h3 class="text-h6">Chat</h3>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<v-sheet
|
|
13
|
+
class="overflow-y-auto flex-grow-1 pa-3"
|
|
14
|
+
:style="{ height: 'calc(100vh - 300px)', overflowX: 'hidden' }"
|
|
15
|
+
>
|
|
16
|
+
<v-row
|
|
17
|
+
v-for="(msg, i) in messages"
|
|
18
|
+
:key="i"
|
|
19
|
+
no-gutters
|
|
20
|
+
:justify="msg.isCurrentUser ? 'end' : 'start'"
|
|
21
|
+
class="mb-2"
|
|
22
|
+
>
|
|
23
|
+
<v-col cols="auto">
|
|
24
|
+
<ChatBubbles
|
|
25
|
+
:isCurrentUser="msg.isCurrentUser"
|
|
26
|
+
:message="msg.message"
|
|
27
|
+
:timestamp="msg.timestamp"
|
|
28
|
+
:seenLabel="msg.seenLabel"
|
|
29
|
+
:senderName="msg.senderName"
|
|
30
|
+
:role="msg.role"
|
|
31
|
+
/>
|
|
32
|
+
</v-col>
|
|
33
|
+
</v-row>
|
|
34
|
+
</v-sheet>
|
|
35
|
+
|
|
36
|
+
<v-card class="mx-4 mb-4 mt-2 pa-3 rounded-lg" elevation="2">
|
|
37
|
+
<v-row no-gutters align="end">
|
|
38
|
+
<v-col cols="10">
|
|
39
|
+
<v-textarea
|
|
40
|
+
label="Message"
|
|
41
|
+
auto-grow
|
|
42
|
+
hide-details
|
|
43
|
+
rows="1"
|
|
44
|
+
variant="outlined"
|
|
45
|
+
/>
|
|
46
|
+
</v-col>
|
|
47
|
+
<v-col cols="2" class="pl-2">
|
|
48
|
+
<v-btn color="primary-button" block class="pa-4 py-6 text-capitalize"
|
|
49
|
+
>Send</v-btn
|
|
50
|
+
>
|
|
51
|
+
</v-col>
|
|
52
|
+
</v-row>
|
|
53
|
+
|
|
54
|
+
<v-divider class="my-2" />
|
|
55
|
+
|
|
56
|
+
<v-row justify="start" no-gutters align="center">
|
|
57
|
+
<v-col cols="auto">
|
|
58
|
+
<v-icon size="20" class="mr-3">mdi-paperclip</v-icon>
|
|
59
|
+
</v-col>
|
|
60
|
+
|
|
61
|
+
<v-col cols="auto">
|
|
62
|
+
<v-icon size="20" class="mr-3">mdi-emoticon-happy-outline</v-icon>
|
|
63
|
+
</v-col>
|
|
64
|
+
|
|
65
|
+
<v-col cols="auto">
|
|
66
|
+
<v-menu location="top" offset="10">
|
|
67
|
+
<template #activator="{ props }">
|
|
68
|
+
<v-btn icon v-bind="props" density="compact" variant="text">
|
|
69
|
+
<v-icon size="20" color="gray">mdi-message-outline</v-icon>
|
|
70
|
+
</v-btn>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<v-card min-width="350" class="rounded-lg">
|
|
74
|
+
<v-card-title class="text-subtitle-1 font-weight-medium pb-1"
|
|
75
|
+
>Quick Chat</v-card-title
|
|
76
|
+
>
|
|
77
|
+
<v-divider class="mb-1" />
|
|
78
|
+
|
|
79
|
+
<v-list density="compact">
|
|
80
|
+
<v-list-item
|
|
81
|
+
v-for="(option, index) in quickChatOptions"
|
|
82
|
+
:key="index"
|
|
83
|
+
@click="selectQuickChat(option)"
|
|
84
|
+
>
|
|
85
|
+
<v-list-item-title>{{ option }}</v-list-item-title>
|
|
86
|
+
</v-list-item>
|
|
87
|
+
</v-list>
|
|
88
|
+
</v-card>
|
|
89
|
+
</v-menu>
|
|
90
|
+
</v-col>
|
|
91
|
+
</v-row>
|
|
92
|
+
</v-card>
|
|
93
|
+
</v-sheet>
|
|
94
|
+
</template>
|
|
95
|
+
|
|
96
|
+
<script setup lang="ts">
|
|
97
|
+
const messages = [
|
|
98
|
+
{
|
|
99
|
+
isCurrentUser: true,
|
|
100
|
+
message: "Hello we would like to transfer this task...",
|
|
101
|
+
timestamp: "06/28/2024 08:40",
|
|
102
|
+
seenLabel: "Seen",
|
|
103
|
+
senderName: "You",
|
|
104
|
+
role: "Site Personnel",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
isCurrentUser: false,
|
|
108
|
+
message: "Sure. Let me take care of that.",
|
|
109
|
+
timestamp: "06/28/2024 08:42",
|
|
110
|
+
senderName: "Tan Wei",
|
|
111
|
+
role: "Manager",
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
isCurrentUser: true,
|
|
115
|
+
message: "Thanks!",
|
|
116
|
+
timestamp: "06/28/2024 08:43",
|
|
117
|
+
seenLabel: "Seen",
|
|
118
|
+
senderName: "You",
|
|
119
|
+
role: "Site Personnel",
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
isCurrentUser: false,
|
|
123
|
+
message: "No worries!",
|
|
124
|
+
timestamp: "06/28/2024 08:44",
|
|
125
|
+
senderName: "Tan Wei",
|
|
126
|
+
role: "Manager",
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
isCurrentUser: true,
|
|
130
|
+
message: "Let me know if you need anything else.",
|
|
131
|
+
timestamp: "06/28/2024 08:45",
|
|
132
|
+
seenLabel: "Seen",
|
|
133
|
+
senderName: "You",
|
|
134
|
+
role: "Site Personnel",
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
const quickChatOptions = [
|
|
139
|
+
"This is all noted, thank you!",
|
|
140
|
+
"Ok, we are working on it.",
|
|
141
|
+
"Yes, we will do it as soon as possible.",
|
|
142
|
+
"Sorry, we cannot make it this time.",
|
|
143
|
+
"We are here to help.",
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
const selectQuickChat = (msg: string) => {
|
|
147
|
+
console.log("Quick Chat Selected:", msg);
|
|
148
|
+
};
|
|
149
|
+
</script>
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-sheet
|
|
3
|
+
elevation="1"
|
|
4
|
+
rounded="0"
|
|
5
|
+
class="pa-4 fill-height"
|
|
6
|
+
style="border-right: 1px solid #e0e0e0; border-top: 1px solid #e0e0e0"
|
|
7
|
+
>
|
|
8
|
+
<h3 class="text-h6 mb-4">{{ title }}</h3>
|
|
9
|
+
|
|
10
|
+
<v-text-field
|
|
11
|
+
v-model="search"
|
|
12
|
+
@input="_search"
|
|
13
|
+
label="Search"
|
|
14
|
+
prepend-inner-icon="mdi-magnify"
|
|
15
|
+
density="compact"
|
|
16
|
+
hide-details
|
|
17
|
+
variant="outlined"
|
|
18
|
+
class="mb-4"
|
|
19
|
+
/>
|
|
20
|
+
|
|
21
|
+
<v-select
|
|
22
|
+
label="Filter by Status"
|
|
23
|
+
:items="['All', 'In Progress', 'To Do', 'Done']"
|
|
24
|
+
v-model="selectedStatus"
|
|
25
|
+
density="compact"
|
|
26
|
+
hide-details
|
|
27
|
+
variant="outlined"
|
|
28
|
+
class="mb-4"
|
|
29
|
+
/>
|
|
30
|
+
|
|
31
|
+
<v-row dense class="mb-4" align="center">
|
|
32
|
+
<v-col cols="5">
|
|
33
|
+
<v-menu
|
|
34
|
+
v-model="startMenu"
|
|
35
|
+
:close-on-content-click="false"
|
|
36
|
+
transition="scale-transition"
|
|
37
|
+
offset-y
|
|
38
|
+
max-width="280px"
|
|
39
|
+
min-width="auto"
|
|
40
|
+
>
|
|
41
|
+
<template #activator="{ props }">
|
|
42
|
+
<v-text-field
|
|
43
|
+
v-bind="props"
|
|
44
|
+
v-model="startDate"
|
|
45
|
+
label="Start Date"
|
|
46
|
+
density="compact"
|
|
47
|
+
variant="outlined"
|
|
48
|
+
readonly
|
|
49
|
+
hide-details
|
|
50
|
+
prepend-inner-icon="mdi-calendar"
|
|
51
|
+
/>
|
|
52
|
+
</template>
|
|
53
|
+
|
|
54
|
+
<v-date-picker
|
|
55
|
+
v-model="startDate"
|
|
56
|
+
@update:model-value="startMenu = false"
|
|
57
|
+
show-adjacent-months
|
|
58
|
+
hide-header
|
|
59
|
+
class="d-flex flex-column pa-2"
|
|
60
|
+
style="font-size: 0.85rem; max-height: 300px"
|
|
61
|
+
/>
|
|
62
|
+
</v-menu>
|
|
63
|
+
</v-col>
|
|
64
|
+
|
|
65
|
+
<v-col cols="2" class="d-flex align-center justify-center">
|
|
66
|
+
<span class="text-subtitle-2">to</span>
|
|
67
|
+
</v-col>
|
|
68
|
+
|
|
69
|
+
<v-col cols="5">
|
|
70
|
+
<v-menu
|
|
71
|
+
v-model="endMenu"
|
|
72
|
+
:close-on-content-click="false"
|
|
73
|
+
transition="scale-transition"
|
|
74
|
+
offset-y
|
|
75
|
+
max-width="280px"
|
|
76
|
+
min-width="auto"
|
|
77
|
+
>
|
|
78
|
+
<template #activator="{ props }">
|
|
79
|
+
<v-text-field
|
|
80
|
+
v-bind="props"
|
|
81
|
+
v-model="endDate"
|
|
82
|
+
label="End Date"
|
|
83
|
+
density="compact"
|
|
84
|
+
variant="outlined"
|
|
85
|
+
readonly
|
|
86
|
+
hide-details
|
|
87
|
+
prepend-inner-icon="mdi-calendar"
|
|
88
|
+
/>
|
|
89
|
+
</template>
|
|
90
|
+
|
|
91
|
+
<v-date-picker
|
|
92
|
+
v-model="endDate"
|
|
93
|
+
@update:model-value="endMenu = false"
|
|
94
|
+
show-adjacent-months
|
|
95
|
+
hide-header
|
|
96
|
+
class="d-flex flex-column pa-2"
|
|
97
|
+
style="font-size: 0.85rem; max-height: 300px"
|
|
98
|
+
/>
|
|
99
|
+
</v-menu>
|
|
100
|
+
</v-col>
|
|
101
|
+
</v-row>
|
|
102
|
+
|
|
103
|
+
<v-sheet
|
|
104
|
+
class="overflow-y-auto flex-grow-1 pa-3"
|
|
105
|
+
:style="{ height: 'calc(100vh - 320px)', overflowX: 'hidden' }"
|
|
106
|
+
>
|
|
107
|
+
<v-row dense>
|
|
108
|
+
<v-col v-for="(item, index) in items" :key="item._id" cols="12">
|
|
109
|
+
<div class="cursor-pointer" @click="$emit('select', item._id)">
|
|
110
|
+
<ChatListCard
|
|
111
|
+
:image="currentUser?.profile"
|
|
112
|
+
:subject="item.subject"
|
|
113
|
+
:status="item.status"
|
|
114
|
+
:createdAt="
|
|
115
|
+
item.createdAt
|
|
116
|
+
? new Date(item.createdAt).toLocaleDateString()
|
|
117
|
+
: ''
|
|
118
|
+
"
|
|
119
|
+
:createdByName="item.createdByName"
|
|
120
|
+
:userProfile="currentUser?.profile"
|
|
121
|
+
:getNameInitials="getNameInitials"
|
|
122
|
+
/>
|
|
123
|
+
</div>
|
|
124
|
+
</v-col>
|
|
125
|
+
</v-row>
|
|
126
|
+
</v-sheet>
|
|
127
|
+
</v-sheet>
|
|
128
|
+
</template>
|
|
129
|
+
|
|
130
|
+
<script setup lang="ts">
|
|
131
|
+
defineProps<{
|
|
132
|
+
title: string;
|
|
133
|
+
items: Array<Record<string, any>>;
|
|
134
|
+
}>();
|
|
135
|
+
|
|
136
|
+
const emit = defineEmits(["select", "search"]);
|
|
137
|
+
|
|
138
|
+
const { currentUser } = useLocalAuth();
|
|
139
|
+
const { getNameInitials, search } = useUtils();
|
|
140
|
+
|
|
141
|
+
const selectedStatus = ref("All");
|
|
142
|
+
const startDate = ref(null);
|
|
143
|
+
const endDate = ref(null);
|
|
144
|
+
const startMenu = ref(false);
|
|
145
|
+
const endMenu = ref(false);
|
|
146
|
+
|
|
147
|
+
function _search() {
|
|
148
|
+
emit("search", search.value);
|
|
149
|
+
}
|
|
150
|
+
</script>
|