@7365admin1/layer-common 1.8.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.
Files changed (198) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.editorconfig +12 -0
  4. package/.github/workflows/main.yml +17 -0
  5. package/.github/workflows/publish.yml +39 -0
  6. package/.nuxtrc +1 -0
  7. package/.playground/app.vue +41 -0
  8. package/.playground/eslint.config.mjs +6 -0
  9. package/.playground/nuxt.config.ts +22 -0
  10. package/.playground/pages/feedback.vue +30 -0
  11. package/CHANGELOG.md +263 -0
  12. package/README.md +73 -0
  13. package/app.vue +3 -0
  14. package/components/AccessCardAddForm.vue +363 -0
  15. package/components/AccessManagement.vue +420 -0
  16. package/components/Avatar/Main.vue +68 -0
  17. package/components/BillingMain.vue +66 -0
  18. package/components/BtnUploadFile.vue +139 -0
  19. package/components/BuildingForm.vue +303 -0
  20. package/components/BuildingManagement/buildings.vue +335 -0
  21. package/components/BuildingManagement/units.vue +350 -0
  22. package/components/BuildingUnitFormAdd.vue +441 -0
  23. package/components/BuildingUnitFormEdit.vue +429 -0
  24. package/components/CameraForm.vue +264 -0
  25. package/components/CameraMain.vue +352 -0
  26. package/components/Card/DeleteConfirmation.vue +51 -0
  27. package/components/Card/MemberInfoSummary.vue +44 -0
  28. package/components/Card/Toggle.vue +25 -0
  29. package/components/Chat/Bubbles.vue +53 -0
  30. package/components/Chat/Information.vue +416 -0
  31. package/components/Chat/ListCard.vue +62 -0
  32. package/components/Chat/Message.vue +158 -0
  33. package/components/Chat/Navigation.vue +150 -0
  34. package/components/ConfirmDialog.vue +66 -0
  35. package/components/Container/Standard.vue +33 -0
  36. package/components/DashboardPlaceholder.vue +1524 -0
  37. package/components/Dialog/DeleteConfirmation.vue +51 -0
  38. package/components/Dialog/ReplaceAutofillPrompt.vue +49 -0
  39. package/components/Dialog/UpdateMoreAction.vue +103 -0
  40. package/components/DocumentForm.vue +187 -0
  41. package/components/DocumentManagement.vue +376 -0
  42. package/components/Editor.vue +95 -0
  43. package/components/EntryPassMain.vue +518 -0
  44. package/components/Feedback/Form.vue +173 -0
  45. package/components/FeedbackDetail.vue +599 -0
  46. package/components/FeedbackMain.vue +588 -0
  47. package/components/FormDialog.vue +65 -0
  48. package/components/ImageCarousel.vue +138 -0
  49. package/components/Input/Date.vue +177 -0
  50. package/components/Input/DateTimePicker.vue +131 -0
  51. package/components/Input/File.vue +236 -0
  52. package/components/Input/FileV2.vue +234 -0
  53. package/components/Input/InputPhoneNumberV2.vue +164 -0
  54. package/components/Input/ListGroupSelection.vue +96 -0
  55. package/components/Input/NRICNumber.vue +53 -0
  56. package/components/Input/NewDate.vue +123 -0
  57. package/components/Input/Number.vue +124 -0
  58. package/components/Input/Password.vue +22 -0
  59. package/components/Input/PhoneNumber.vue +188 -0
  60. package/components/Input/VehicleNumber.vue +49 -0
  61. package/components/InputLabel.vue +22 -0
  62. package/components/InvitationForm.vue +359 -0
  63. package/components/InvitationMain.vue +310 -0
  64. package/components/Layout/Header.vue +129 -0
  65. package/components/Layout/NavigationDrawer.vue +44 -0
  66. package/components/ListItem.vue +35 -0
  67. package/components/ListView.vue +87 -0
  68. package/components/LocalPagination.vue +31 -0
  69. package/components/MemberMain.vue +459 -0
  70. package/components/NFC/NFCPatrolReportMain.vue +591 -0
  71. package/components/NFC/NFCPatrolRouteForm.vue +596 -0
  72. package/components/NFC/NFCPatrolRouteMain.vue +539 -0
  73. package/components/NFC/NFCTagForm.vue +236 -0
  74. package/components/NFC/NFCTagMain.vue +337 -0
  75. package/components/NFC/PatrolSettings.vue +130 -0
  76. package/components/NavigationItem.vue +83 -0
  77. package/components/NumberSettingField.vue +107 -0
  78. package/components/OnlineFormConfigurationForm.vue +290 -0
  79. package/components/OnlineFormsConfiguration.vue +429 -0
  80. package/components/PeopleForm.vue +452 -0
  81. package/components/PlaceholderComponent.vue +34 -0
  82. package/components/RolePermissionFormCreate.vue +161 -0
  83. package/components/RolePermissionFormPreviewUpdate.vue +183 -0
  84. package/components/RolePermissionMain.vue +361 -0
  85. package/components/SearchVehicleNumberUser.vue +91 -0
  86. package/components/ServiceProviderFormCreate.vue +154 -0
  87. package/components/ServiceProviderMain.vue +547 -0
  88. package/components/SignaturePad.vue +73 -0
  89. package/components/Snackbar.vue +23 -0
  90. package/components/SpecificAttr.vue +53 -0
  91. package/components/SupplyManagement.vue +292 -0
  92. package/components/SwitchContext.vue +108 -0
  93. package/components/TableList.vue +150 -0
  94. package/components/TableListSecondary.vue +164 -0
  95. package/components/TableMain.vue +142 -0
  96. package/components/TableWithButton.vue +94 -0
  97. package/components/VehicleUpdateMoreAction.vue +84 -0
  98. package/components/VideoPlayer.vue +125 -0
  99. package/components/VisitorForm.vue +659 -0
  100. package/components/VisitorFormSelection.vue +53 -0
  101. package/components/VisitorManagement.vue +490 -0
  102. package/components/WorkOrder/Create.vue +284 -0
  103. package/components/WorkOrder/Detail.vue +71 -0
  104. package/components/WorkOrder/ListView.vue +96 -0
  105. package/components/WorkOrder/Main.vue +489 -0
  106. package/components/Workorder.vue +1 -0
  107. package/composables/useAddress.ts +107 -0
  108. package/composables/useBuilding.ts +250 -0
  109. package/composables/useBuildingUnit.ts +116 -0
  110. package/composables/useCard.ts +46 -0
  111. package/composables/useCommonPermission.ts +207 -0
  112. package/composables/useCustomer.ts +113 -0
  113. package/composables/useCustomerSite.ts +56 -0
  114. package/composables/useDashboard.ts +31 -0
  115. package/composables/useDashboardData.ts +425 -0
  116. package/composables/useDocument.ts +57 -0
  117. package/composables/useFacility.ts +246 -0
  118. package/composables/useFeedback.ts +119 -0
  119. package/composables/useFile.ts +55 -0
  120. package/composables/useInvoice.ts +18 -0
  121. package/composables/useLocal.ts +131 -0
  122. package/composables/useLocalAuth.ts +137 -0
  123. package/composables/useLocalSetup.ts +13 -0
  124. package/composables/useMember.ts +111 -0
  125. package/composables/useNFCPatrolRoute.ts +77 -0
  126. package/composables/useNFCPatrolSettings.ts +19 -0
  127. package/composables/useNFCPatrolTag.ts +53 -0
  128. package/composables/useOnlineForm.ts +67 -0
  129. package/composables/useOrg.ts +129 -0
  130. package/composables/usePDFDownload.ts +25 -0
  131. package/composables/usePaymentMethod.ts +101 -0
  132. package/composables/usePeople.ts +81 -0
  133. package/composables/usePermission.ts +54 -0
  134. package/composables/usePhoneCountries.ts +561 -0
  135. package/composables/usePrice.ts +15 -0
  136. package/composables/usePromoCode.ts +36 -0
  137. package/composables/useRecapPermission.ts +26 -0
  138. package/composables/useRole.ts +104 -0
  139. package/composables/useSecurityUtils.ts +18 -0
  140. package/composables/useServiceProvider.ts +224 -0
  141. package/composables/useSite.ts +109 -0
  142. package/composables/useSiteEntryPassSettings.ts +46 -0
  143. package/composables/useSiteSettings.ts +123 -0
  144. package/composables/useSubscription.ts +150 -0
  145. package/composables/useUser.ts +132 -0
  146. package/composables/useUtils.ts +445 -0
  147. package/composables/useVerification.ts +34 -0
  148. package/composables/useVisitor.ts +120 -0
  149. package/composables/useWorkOrder.ts +85 -0
  150. package/error.vue +41 -0
  151. package/layouts/plain.vue +7 -0
  152. package/middleware/01.auth.ts +20 -0
  153. package/middleware/02.org.ts +21 -0
  154. package/middleware/03.customer.ts +13 -0
  155. package/middleware/member.ts +4 -0
  156. package/nuxt.config.ts +54 -0
  157. package/package.json +39 -0
  158. package/pages/index.vue +3 -0
  159. package/pages/payment-method-linked.vue +31 -0
  160. package/pages/require-customer.vue +56 -0
  161. package/pages/require-organization-membership.vue +47 -0
  162. package/pages/unauthorized.vue +29 -0
  163. package/plugins/API.ts +21 -0
  164. package/plugins/iconify.client.ts +5 -0
  165. package/plugins/secure-member.client.ts +86 -0
  166. package/plugins/vuetify.ts +62 -0
  167. package/public/bg-camera.jpg +0 -0
  168. package/public/bg-city.jpg +0 -0
  169. package/public/bg-condo.jpg +0 -0
  170. package/public/images/icons/delete-icon.png +0 -0
  171. package/public/sprite.svg +1 -0
  172. package/tsconfig.json +3 -0
  173. package/types/address.d.ts +13 -0
  174. package/types/building.d.ts +27 -0
  175. package/types/camera.d.ts +31 -0
  176. package/types/card.d.ts +22 -0
  177. package/types/customer.d.ts +27 -0
  178. package/types/document.d.ts +6 -0
  179. package/types/feedback.d.ts +68 -0
  180. package/types/local.d.ts +74 -0
  181. package/types/member.d.ts +21 -0
  182. package/types/online-form.d.ts +15 -0
  183. package/types/org.d.ts +13 -0
  184. package/types/people.d.ts +24 -0
  185. package/types/permission.d.ts +25 -0
  186. package/types/phone-number.d.ts +10 -0
  187. package/types/price.d.ts +17 -0
  188. package/types/promo-code.d.ts +19 -0
  189. package/types/role.d.ts +11 -0
  190. package/types/select.d.ts +4 -0
  191. package/types/service-provider.d.ts +15 -0
  192. package/types/site.d.ts +20 -0
  193. package/types/subscription.d.ts +23 -0
  194. package/types/user.d.ts +19 -0
  195. package/types/verification.d.ts +20 -0
  196. package/types/visitor.d.ts +42 -0
  197. package/types/work-order.d.ts +42 -0
  198. package/utils/phoneMasks.ts +1703 -0
@@ -0,0 +1,164 @@
1
+ <template>
2
+ <v-card width="100%" variant="outlined" border="thin" rounded="lg">
3
+ <v-toolbar density="compact" class="px-3 border-b-sm" color="transparent">
4
+ <template #prepend>
5
+ <slot name="title">Title</slot>
6
+ </template>
7
+
8
+ <template #append>
9
+ <v-row no-gutters justify="end" align="center">
10
+ <slot name="action-button"></slot>
11
+ </v-row>
12
+ </template>
13
+
14
+ <template v-if="$slots.extension" v-slot:extension>
15
+ <slot name="extension"></slot>
16
+ </template>
17
+ </v-toolbar>
18
+
19
+ <v-data-table
20
+ v-model="selected"
21
+ :headers="props.headers"
22
+ :items="props.items"
23
+ item-value="_id"
24
+ items-per-page="20"
25
+ fixed-header
26
+ hide-default-footer
27
+ selec
28
+ :loading="props.loading"
29
+ @click:row="handleRowClick"
30
+ :style="`max-height:${props.height}`"
31
+ :class="{ 'clickable-rows': props.clickableRows }"
32
+ >
33
+ <template v-if="$slots.item" v-slot:item>
34
+ <slot name="item" :items="props.items"></slot>
35
+ </template>
36
+
37
+ <template v-slot:item="{ item }">
38
+ <tr @click="handleRowClick(item)">
39
+ <td v-for="col in props.headers" :key="col.value">
40
+ <slot :name="col.value" :item="item">
41
+ {{ item[col.value] }}
42
+ </slot>
43
+ </td>
44
+ </tr>
45
+ </template>
46
+ </v-data-table>
47
+ <v-toolbar density="compact" class="px-3 border-t-sm" color="transparent">
48
+ <template #append>
49
+ <v-row no-gutters justify="end" align="center">
50
+ <span class="mr-2 text-caption text-fontgray">
51
+ {{ props.pageRange }}
52
+ </span>
53
+ <div class="arrow-navigation">
54
+ <v-btn
55
+ icon="mdi-chevron-left"
56
+ variant="text"
57
+ density="comfortable"
58
+ :disabled="page <= 1"
59
+ @click="decrement"
60
+ />
61
+ <v-btn
62
+ icon="mdi-chevron-right"
63
+ variant="text"
64
+ density="comfortable"
65
+ :disabled="page >= props.length"
66
+ @click="increment"
67
+ />
68
+ </div>
69
+ </v-row>
70
+ </template>
71
+
72
+ <template v-if="$slots.extension" v-slot:extension>
73
+ <slot name="extension"></slot>
74
+ </template>
75
+ </v-toolbar>
76
+ </v-card>
77
+ </template>
78
+
79
+ <script setup lang="ts">
80
+ const selected = defineModel({
81
+ type: Array as PropType<Array<string>>,
82
+ default: () => [],
83
+ });
84
+ const page = defineModel("page", { type: Number, default: 0 });
85
+
86
+ const props = defineProps({
87
+ headers: {
88
+ type: Array as PropType<Array<Record<string, string>>>,
89
+ required: true,
90
+ default: () => [],
91
+ },
92
+ items: {
93
+ type: Array as PropType<Array<Record<string, any>>>,
94
+ required: true,
95
+ default: () => [],
96
+ },
97
+ pages: {
98
+ type: Number,
99
+ required: true,
100
+ default: 0,
101
+ },
102
+ pageRange: {
103
+ type: String,
104
+ required: true,
105
+ default: "-- - -- of --",
106
+ },
107
+ length: {
108
+ type: Number,
109
+ required: true,
110
+ default: 0,
111
+ },
112
+ loading: {
113
+ type: Boolean,
114
+ required: true,
115
+ default: true,
116
+ },
117
+ height: {
118
+ type: String,
119
+ default: "500px",
120
+ },
121
+ clickableRows: {
122
+ type: Boolean,
123
+ default: false,
124
+ },
125
+ });
126
+
127
+ const emit = defineEmits(["refresh", "update:pagination", "row-click"]);
128
+
129
+ function increment() {
130
+ page.value++;
131
+ emit("update:pagination", page.value);
132
+ }
133
+
134
+ function decrement() {
135
+ page.value--;
136
+ emit("update:pagination", page.value);
137
+ }
138
+
139
+ function handleRowClick(item: any) {
140
+ if (props.clickableRows) {
141
+ emit("row-click", item);
142
+ }
143
+ }
144
+
145
+ const selectAll = ref(false);
146
+
147
+ watch(selectAll, (curr) => {
148
+ selected.value.splice(0, selected.value.length);
149
+ if (curr) {
150
+ const ids = props.items.map((i) => i._id as string);
151
+ selected.value.push(...ids);
152
+ }
153
+ });
154
+ </script>
155
+
156
+ <style scoped>
157
+ .clickable-rows tbody tr {
158
+ cursor: pointer;
159
+ }
160
+
161
+ .clickable-rows tbody tr:hover {
162
+ background-color: #f5f5f5;
163
+ }
164
+ </style>
@@ -0,0 +1,142 @@
1
+ <template>
2
+ <v-row no-gutters>
3
+ <!-- Top Actions -->
4
+ <v-col cols="12" class="mb-2" v-if="(canCreate || $slots.actions)">
5
+ <v-row no-gutters>
6
+ <slot name="actions">
7
+ <v-col cols="12">
8
+ <v-row no-gutters justify="space-between">
9
+ <v-btn v-if="canCreate" class="text-none" rounded="pill" variant="tonal" size="large"
10
+ @click="emits('create')">
11
+ {{ createLabel }}
12
+ </v-btn>
13
+ <v-text-field v-if="props.canSearch" v-model="searchInput" density="compact" placeholder="Search" clearable max-width="300"
14
+ append-inner-icon="mdi-magnify" hide-details />
15
+ </v-row>
16
+ </v-col>
17
+ </slot>
18
+
19
+ </v-row>
20
+ </v-col>
21
+
22
+ <!-- Table Card -->
23
+ <v-col cols="12">
24
+ <v-card width="100%" variant="outlined" border="thin" rounded="lg" :loading="loading">
25
+ <!-- Toolbar -->
26
+ <v-toolbar density="compact" color="grey-lighten-4" :extension-height="extensionHeight">
27
+ <template #prepend>
28
+ <v-btn fab icon density="comfortable" @click="emits('refresh')">
29
+ <v-icon>mdi-refresh</v-icon>
30
+ </v-btn>
31
+ <slot name="prepend-additional" />
32
+ </template>
33
+
34
+ <template #append>
35
+ <v-row no-gutters justify="end" align="center">
36
+ <span class="mr-2 text-caption text-fontgray">
37
+ {{ pageRange }}
38
+ </span>
39
+ <local-pagination v-model="internalPage" :length="pages"
40
+ @update:value="emits('update:page', internalPage)" />
41
+ </v-row>
42
+ </template>
43
+
44
+ <template v-if="$slots.extension" #extension>
45
+ <slot name="extension" />
46
+ </template>
47
+ </v-toolbar>
48
+
49
+
50
+ <!-- Data Table -->
51
+ <v-data-table :headers="headers" :items="items" :item-value="itemValue" :items-per-page="itemsPerPage"
52
+ fixed-header hide-default-footer :hide-default-header="!showHeader"
53
+ @click:row="(_: any, data: any) => emits('row-click', data)"
54
+ :style="`max-height: calc(100vh - (${offset}px))`">
55
+ <template v-for="(_, slotName) in $slots" #[slotName]="slotProps">
56
+ <slot :name="slotName" v-bind="slotProps" />
57
+ </template>
58
+ </v-data-table>
59
+ <slot name="footer" />
60
+ </v-card>
61
+ </v-col>
62
+ </v-row>
63
+ </template>
64
+
65
+ <script lang="ts" setup>
66
+ const props = defineProps({
67
+ headers: {
68
+ type: Array as PropType<Array<Record<string, any>>>,
69
+ required: true,
70
+ },
71
+ items: {
72
+ type: Array as PropType<Array<Record<string, any>>>,
73
+ default: () => [],
74
+ },
75
+ loading: {
76
+ type: Boolean,
77
+ default: false,
78
+ },
79
+ canCreate: {
80
+ type: Boolean,
81
+ default: false,
82
+ },
83
+ canSearch: {
84
+ type: Boolean,
85
+ default: false,
86
+ },
87
+ createLabel: {
88
+ type: String,
89
+ default: "Add",
90
+ },
91
+ itemValue: {
92
+ type: String,
93
+ default: "_id",
94
+ },
95
+ itemsPerPage: {
96
+ type: Number,
97
+ default: 20,
98
+ },
99
+ page: {
100
+ type: Number,
101
+ default: 1,
102
+ },
103
+ pages: {
104
+ type: Number,
105
+ default: 0,
106
+ },
107
+ pageRange: {
108
+ type: String,
109
+ default: "-- - -- of --",
110
+ },
111
+ showHeader: {
112
+ type: Boolean,
113
+ default: false
114
+ },
115
+ extensionHeight: {
116
+ type: Number,
117
+ default: 50
118
+ },
119
+ offset: {
120
+ type: Number,
121
+ default: 200
122
+ }
123
+ });
124
+
125
+ const emits = defineEmits(["create", "refresh", "update:page", "row-click"]);
126
+
127
+ const searchInput = defineModel('search', { default: "" })
128
+
129
+ const internalPage = ref(props.page);
130
+ watch(
131
+ () => props.page,
132
+ (val) => {
133
+ internalPage.value = val;
134
+ }
135
+ );
136
+ </script>
137
+
138
+ <style scoped>
139
+ .v-data-table :deep(thead th) {
140
+ font-weight: bold !important;
141
+ }
142
+ </style>
@@ -0,0 +1,94 @@
1
+ <template>
2
+ <v-card flat border="sm black" :loading="loading" class="px-0" :ripple="false">
3
+ <v-toolbar class="px-5">
4
+ <template #prepend>
5
+ <span class="text-subtitle-1 font-weight-black">{{ tableTitle }}</span>
6
+ </template>
7
+ <template #append>
8
+ <v-btn color="primary" variant="flat" size="large" density="comfortable" :text="buttonLabel"
9
+ class="text-capitalize" />
10
+ </template>
11
+ </v-toolbar>
12
+ <v-card-text class="pa-0 bg-red">
13
+ <v-data-table :headers="headers" :items="items" :item-value="itemValue" :items-per-page="itemsPerPage"
14
+ fixed-header hide-default-footer hide-default-header @click:row="$emit('row-click', $event)"
15
+ style="max-height: calc(100vh - (200px))">
16
+ <template v-for="(_, slotName) in $slots" #[slotName]="slotProps">
17
+ <slot :name="slotName" v-bind="slotProps" />
18
+ </template>
19
+ </v-data-table>
20
+ </v-card-text>
21
+ </v-card>
22
+ </template>
23
+
24
+ <script setup lang="ts">
25
+ const selected = defineModel({
26
+ type: Array as PropType<Array<string>>,
27
+ default: () => [],
28
+ });
29
+ const page = defineModel("page", { type: Number, default: 0 });
30
+
31
+ const props = defineProps({
32
+ headers: {
33
+ type: Array as PropType<Array<Record<string, string | number>>>,
34
+ required: true,
35
+ default: () => [],
36
+ },
37
+ items: {
38
+ type: Array as PropType<Array<Record<string, any>>>,
39
+ required: true,
40
+ default: () => [],
41
+ },
42
+
43
+ itemValue: {
44
+ type: String,
45
+ default: "_id",
46
+ },
47
+ itemsPerPage: {
48
+ type: Number,
49
+ default: 20,
50
+ },
51
+ loading: {
52
+ type: Boolean,
53
+ required: true,
54
+ default: true,
55
+ },
56
+ tableTitle: {
57
+ type: String,
58
+ default: "",
59
+ required: false
60
+ },
61
+ buttonLabel: {
62
+ type: String,
63
+ default: "Add",
64
+ required: false
65
+ }
66
+ });
67
+
68
+ const emit = defineEmits(["refresh", "update:pagination", "row-click"]);
69
+
70
+ function increment() {
71
+ page.value++;
72
+ emit("update:pagination", page.value);
73
+ }
74
+
75
+ function decrement() {
76
+ page.value--;
77
+ emit("update:pagination", page.value);
78
+ }
79
+
80
+ function tableRowClickHandler(_: any, row: any) {
81
+ const item = props.items[row.index];
82
+ emit("row-click", item);
83
+ }
84
+
85
+ const selectAll = ref(false);
86
+
87
+ watch(selectAll, (curr) => {
88
+ selected.value.splice(0, selected.value.length);
89
+ if (curr) {
90
+ const ids = props.items.map((i) => i._id as string);
91
+ selected.value.push(...ids);
92
+ }
93
+ });
94
+ </script>
@@ -0,0 +1,84 @@
1
+ <template>
2
+ <v-card width="100%">
3
+ <v-toolbar>
4
+ <template v-if="$slots.header">
5
+ <slot name="header" :title="title" :onClose="onClose" />
6
+ </template>
7
+ <template v-else>
8
+ <v-row no-gutters class="fill-height px-6" align="center">
9
+ <span class="font-weight-bold text-h5 text-capitalize">{{
10
+ title
11
+ }}</span>
12
+ </v-row>
13
+ </template>
14
+ </v-toolbar>
15
+
16
+ <v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0">
17
+ <slot name="content" />
18
+ </v-card-text>
19
+
20
+ <v-toolbar class="pa-0" density="compact">
21
+ <v-row no-gutters>
22
+ <v-col :cols="canDelete || canUpdate ? 6 : 12" class="pa-0">
23
+ <v-btn block variant="text" class="text-none" size="large" @click="emit('close')" height="48">
24
+ Close
25
+ </v-btn>
26
+ </v-col>
27
+
28
+ <v-col cols="6" class="pa-0" v-if="canDelete || canUpdate">
29
+ <v-menu>
30
+ <template #activator="{ props }">
31
+ <v-btn block variant="flat" color="black" class="text-none" height="48" v-bind="props" tile>
32
+ More actions
33
+ </v-btn>
34
+ </template>
35
+
36
+ <v-list class="pa-0">
37
+ <v-list-item v-if="canUpdate" @click="emit('edit')">
38
+ <v-list-item-title class="text-subtitle-2">
39
+ {{ editButtonLabel }}
40
+ </v-list-item-title>
41
+ </v-list-item>
42
+
43
+ <v-list-item v-if="canDelete" @click="emit('delete')" class="text-red">
44
+ <v-list-item-title class="text-subtitle-2">
45
+ {{ deleteButtonLabel }}
46
+ </v-list-item-title>
47
+ </v-list-item>
48
+ </v-list>
49
+ </v-menu>
50
+ </v-col>
51
+ </v-row>
52
+ </v-toolbar>
53
+ </v-card>
54
+ </template>
55
+
56
+ <script setup lang="ts">
57
+ const prop = defineProps({
58
+ canUpdate: {
59
+ type: Boolean,
60
+ default: true,
61
+ },
62
+ canDelete: {
63
+ type: Boolean,
64
+ default: true,
65
+ },
66
+ editButtonLabel: {
67
+ type: String,
68
+ default: "Edit",
69
+ },
70
+ deleteButtonLabel: {
71
+ type: String,
72
+ default: "Delete",
73
+ },
74
+ title: {
75
+ type: String,
76
+ default: "Details",
77
+ },
78
+ });
79
+
80
+ const emit = defineEmits(["close", "edit", "delete"]);
81
+ const { canUpdate, editButtonLabel, deleteButtonLabel, title } = prop;
82
+ </script>
83
+
84
+ <style scoped></style>
@@ -0,0 +1,125 @@
1
+ <template>
2
+ <v-dialog v-model="overlay" v-if="overlay" width="100%" height="100%" opacity="50">
3
+ <v-row align="center" justify="center" class="fill-height" style="position: relative;">
4
+ <v-carousel hide-delimiters width="100%" height="100%" :show-arrows="files.length > 1 ? 'hover' : false"
5
+ v-model="activeIndex">
6
+ <template v-for="x, index in files" :key="x || index">
7
+ <template v-if="fileTypes?.[x] === 'video'">
8
+ <v-carousel-item>
9
+ <v-row no-gutters class="h-100 w-100" align="center" justify="center">
10
+ <video width="80%" height="80%" controls>
11
+ <source :src="getFileUrl(x)" />
12
+ </video>
13
+ </v-row>
14
+ <template v-slot:placeholder>
15
+ <div class="d-flex align-center justify-center fill-height">
16
+ <v-progress-circular color="grey-lighten-4" indeterminate></v-progress-circular>
17
+ </div>
18
+ </template>
19
+ </v-carousel-item>
20
+ </template>
21
+ <template v-else>
22
+ <v-carousel-item>
23
+ <v-row no-gutters class="h-100 w-100" align="center" justify="center">
24
+ <v-icon size="100" color="white">mdi-file</v-icon>
25
+ </v-row>
26
+ </v-carousel-item>
27
+ </template>
28
+ </template>
29
+
30
+ <template v-slot:prev="{ props }">
31
+ <v-btn color="white" variant="outlined" class="text-white" icon="mdi-chevron-left"
32
+ @click="props.onClick"></v-btn>
33
+ </template>
34
+ <template v-slot:next="{ props }">
35
+ <v-btn color="white" class="text-white" variant="outlined" icon="mdi-chevron-right"
36
+ @click="props.onClick"></v-btn>
37
+ </template>
38
+ </v-carousel>
39
+ <div style="position: absolute; top: 2%; left: 0%; right: 0%; z-index: 2"
40
+ class="cursor-pointer text-white text-h6 custom-shadow d-flex justify-space-between" @click="overlay = false">
41
+ <v-row no-gutters style="position: relative">
42
+ <v-col cols="8" xs="6" sm="2" md="2" lg="2">
43
+ <v-btn prepend-icon="mdi-close" text="Close" rounded="lg" color="secondary" class="ml-2"
44
+ style="position: absolute;"></v-btn>
45
+ </v-col>
46
+
47
+ </v-row>
48
+
49
+ </div>
50
+ <span class="text-white text-16px d-flex w-100 mt-2 justify-center"
51
+ style="position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%);">{{ `${activeIndex +
52
+ 1}/${files.length}`
53
+ }}</span>
54
+
55
+ </v-row>
56
+ </v-dialog>
57
+ </template>
58
+
59
+ <script setup lang="ts">
60
+ const props = defineProps({
61
+ activeFileId: {
62
+ type: String,
63
+ required: false,
64
+ },
65
+ files: {
66
+ type: Array as PropType<string[]>,
67
+ default: []
68
+ }
69
+ });
70
+
71
+
72
+ const { getFileUrl, urlToFile } = useFile()
73
+ const overlay = defineModel({ required: true, default: false });
74
+ const activeIndex = ref(0);
75
+ const fileTypes = ref<Record<string, "image" | "video" | "other">>({});
76
+
77
+
78
+ const emit = defineEmits(['share', 'like'])
79
+
80
+ watchEffect(() => {
81
+ if (props.activeFileId && props.files.length > 0) {
82
+ const index =
83
+ props.files?.findIndex((x) => x == props.activeFileId) || 0;
84
+ if (index !== -1) {
85
+ activeIndex.value = index;
86
+ } else activeIndex.value = 0;
87
+ } else {
88
+ return (activeIndex.value = 0);
89
+ }
90
+ });
91
+
92
+ async function resolveFileTypes() {
93
+ fileTypes.value = {}; // reset
94
+
95
+ for (const x of props.files) {
96
+ try {
97
+ const url = getFileUrl(x);
98
+ const file = await urlToFile(url, x);
99
+ const type = file?.type;
100
+
101
+
102
+ if (type?.startsWith("video")) fileTypes.value[x] = "video";
103
+ else if (type?.startsWith("image")) fileTypes.value[x] = "image";
104
+ else fileTypes.value[x] = "other";
105
+
106
+ } catch (err) {
107
+ fileTypes.value[x] = "other";
108
+ }
109
+ }
110
+ }
111
+
112
+ watch(
113
+ () => props.files,
114
+ () => resolveFileTypes(),
115
+ { immediate: true }
116
+ );
117
+
118
+
119
+ </script>
120
+
121
+ <style scoped>
122
+ .custom-shadow {
123
+ text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.8);
124
+ }
125
+ </style>