@open-commune/web 0.1.3
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/README.md +41 -0
- package/app.config.ts +10 -0
- package/app.vue +13 -0
- package/assets/css/main.css +13 -0
- package/components/data/display/row/data-avatar.vue +23 -0
- package/components/data/display/row/data-badge.vue +34 -0
- package/components/data/display/row/data-color.vue +17 -0
- package/components/data/display/row/data-display-badge-publish-status.vue +29 -0
- package/components/data/display/row/data-label.vue +37 -0
- package/components/data/display/row/data-last-update.vue +45 -0
- package/components/data/display/row/data-spacer.vue +4 -0
- package/components/data/display/row/data-summary.vue +44 -0
- package/components/data/display/row/data-text.vue +14 -0
- package/components/data/display/row/data-toggle.vue +16 -0
- package/components/data/display/row.vue +111 -0
- package/components/data/lists/data-list.vue +85 -0
- package/components/data/lists/data-lists-table.vue +155 -0
- package/components/elements/avatars/avatar-circular.vue +68 -0
- package/components/elements/avatars/avatar-with-text.vue +47 -0
- package/components/elements/badges/badge.vue +85 -0
- package/components/elements/buttons/primary.vue +32 -0
- package/components/elements/buttons/secondary.vue +35 -0
- package/components/elements/buttons/tertiary.vue +25 -0
- package/components/elements/dropdowns/dots.vue +75 -0
- package/components/elements/media/image.vue +82 -0
- package/components/elements/notifications/default.vue +90 -0
- package/components/forms/handlers/handler-collections.vue +189 -0
- package/components/forms/handlers/handler-publication-status.vue +193 -0
- package/components/forms/handlers/handler-records.vue +138 -0
- package/components/forms/handlers/handler-roles.vue +176 -0
- package/components/forms/handlers/handler-schedules.vue +218 -0
- package/components/forms/handlers/handler-steps.vue +147 -0
- package/components/forms/handlers/handler-tokens.vue +79 -0
- package/components/forms/inputs/input-color-picker.vue +159 -0
- package/components/forms/inputs/input-editor.vue +363 -0
- package/components/forms/inputs/input-files-media.vue +150 -0
- package/components/forms/inputs/input-files.vue +98 -0
- package/components/forms/inputs/input-json.vue +65 -0
- package/components/forms/inputs/input-location.vue +179 -0
- package/components/forms/inputs/input-text-area.vue +65 -0
- package/components/forms/inputs/input-text.vue +122 -0
- package/components/forms/inputs/input-toggle.vue +125 -0
- package/components/forms/inputs/media-section/media-editor.vue +95 -0
- package/components/forms/layouts/form.vue +203 -0
- package/components/forms/layouts/layout.vue +47 -0
- package/components/forms/layouts/modal.vue +96 -0
- package/components/forms/layouts/navigation.vue +48 -0
- package/components/forms/layouts/section.vue +118 -0
- package/components/forms/selects/select-collection-enum.vue +195 -0
- package/components/forms/selects/select-date.vue +245 -0
- package/components/forms/selects/select-enum.vue +186 -0
- package/components/forms/selects/select-record.vue +274 -0
- package/components/layout/headings/headings-collection.vue +29 -0
- package/components/layout/headings/headings-record.vue +33 -0
- package/components/layout/navigations/primary/dropdown-publish.vue +147 -0
- package/components/layout/navigations/primary.vue +132 -0
- package/components/layout/notifications/notifications-list.vue +19 -0
- package/composables/core/domains/use-definitions.ts +57 -0
- package/composables/core/domains/use-entries.ts +62 -0
- package/composables/core/domains/use-media.ts +82 -0
- package/composables/core/use-core.ts +12 -0
- package/composables/use-ability.js +38 -0
- package/composables/use-models.js +27 -0
- package/composables/use-session.ts +80 -0
- package/composables/use-timer.js +62 -0
- package/composables/use-trpc.js +17 -0
- package/editor/index.ts +44 -0
- package/editor/views/CalloutView.vue +22 -0
- package/editor/views/DetailsContentView.vue +11 -0
- package/editor/views/DetailsSummaryView.vue +11 -0
- package/editor/views/DetailsView.vue +72 -0
- package/editor/views/EmbedView.vue +53 -0
- package/editor/views/FileView.vue +79 -0
- package/editor/views/ImageView.vue +56 -0
- package/editor/views/UploadView.vue +71 -0
- package/layouts/default.vue +21 -0
- package/layouts/entrance.vue +10 -0
- package/layouts/record.vue +21 -0
- package/middleware/00.auth.global.js +16 -0
- package/middleware/01.instance.global.js +21 -0
- package/middleware/03.verify-permissions.global.js +11 -0
- package/middleware/04.prefetch-page.global.js +18 -0
- package/middleware/05.headings-page.global.js +25 -0
- package/models/_commun.js +243 -0
- package/models/_factory.js +34 -0
- package/models/collection.js +87 -0
- package/models/entrance.js +42 -0
- package/models/media.js +82 -0
- package/models/organization.js +92 -0
- package/models/record.js +165 -0
- package/models/role.js +66 -0
- package/models/token.js +56 -0
- package/models/user.js +97 -0
- package/nuxt.config.ts +151 -0
- package/package.json +71 -0
- package/pages/[collection]/[record]/index.vue +88 -0
- package/pages/[collection]/index.vue +55 -0
- package/pages/_lab/editor.vue +149 -0
- package/pages/auth/callback.vue +31 -0
- package/pages/index/index.vue +85 -0
- package/pages/index/password/define/[token].vue +79 -0
- package/pages/index/password/recover/index.vue +72 -0
- package/pages/index/welcome/[token].vue +90 -0
- package/pages/index.vue +44 -0
- package/pages/management/index.vue +15 -0
- package/pages/management/members.vue +176 -0
- package/pages/management/organizations.vue +192 -0
- package/pages/management/roles.vue +183 -0
- package/pages/overview.vue +15 -0
- package/pages/settings.vue +197 -0
- package/plugins/01.session.client.ts +5 -0
- package/plugins/02.vue-query.ts +25 -0
- package/public/favicon.ico +0 -0
- package/public/logo-black.svg +1 -0
- package/public/logo-white.svg +1 -0
- package/server/api/trpc/[...trpc].ts +28 -0
- package/server/api/v1/content/deployment.get.ts +45 -0
- package/server/api/v1/content/records.get.ts +12 -0
- package/server/middleware/0.security.ts +19 -0
- package/server/middleware/1.cors.ts +31 -0
- package/server/middleware/2.session.ts +20 -0
- package/server/middleware/3.api-token.ts +20 -0
- package/server/plugins/core.ts +8 -0
- package/server/routes/_tasks/[name].post.ts +20 -0
- package/server/routes/health.get.ts +6 -0
- package/server/tasks/deploy.ts +26 -0
- package/server/types.d.ts +8 -0
- package/server/utils/core.ts +18 -0
- package/store/layout/headings.js +21 -0
- package/store/layout/modal.js +80 -0
- package/store/layout/notifications.js +52 -0
- package/store/layout/workspace.js +80 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="relative block w-full">
|
|
3
|
+
<label
|
|
4
|
+
v-if="label"
|
|
5
|
+
id="listbox-label"
|
|
6
|
+
class="block text-2xs font-light tracking-wider text-gray-500 uppercase"
|
|
7
|
+
>
|
|
8
|
+
{{ label }}
|
|
9
|
+
</label>
|
|
10
|
+
<list-table
|
|
11
|
+
v-if="periodsDataList.length"
|
|
12
|
+
:headers="TABLE_HEADERS"
|
|
13
|
+
:items="periodsDataList"
|
|
14
|
+
:actions="['remove']"
|
|
15
|
+
:class="{ 'mt-1': label }"
|
|
16
|
+
class="col-span-4 bg-gray-100 p-2"
|
|
17
|
+
@remove="_removePeriod"
|
|
18
|
+
/>
|
|
19
|
+
<div v-if="displayPeriodHandler" class="flex flex-col space-y-1 my-4">
|
|
20
|
+
<div class="inline-flex w-full items-center space-x-2">
|
|
21
|
+
<select-date
|
|
22
|
+
:time-picker="enableTime"
|
|
23
|
+
:time-end="!enableToDate"
|
|
24
|
+
:value="periodFromDate"
|
|
25
|
+
class="w-1/4"
|
|
26
|
+
@change="_updateCurrentPeriod('begin', $event)"
|
|
27
|
+
/>
|
|
28
|
+
<select-date
|
|
29
|
+
v-if="enableToDate"
|
|
30
|
+
:time-picker="enableTime"
|
|
31
|
+
:value="periodToDate"
|
|
32
|
+
class="w-1/4"
|
|
33
|
+
@change="_updateCurrentPeriod('end', $event)"
|
|
34
|
+
/>
|
|
35
|
+
<select-enum
|
|
36
|
+
v-if="enablePeriodicity"
|
|
37
|
+
:items="PERIODICITY"
|
|
38
|
+
:item-key="'label'"
|
|
39
|
+
:item-value="'value'"
|
|
40
|
+
:value="periodPeriodicity"
|
|
41
|
+
place-holder="Récurrence"
|
|
42
|
+
class="w-1/4"
|
|
43
|
+
@change="periodPeriodicity = $event"
|
|
44
|
+
/>
|
|
45
|
+
<button-secondary label="Ajouter date(s)" class="whitespace-nowrap" @click="_addPeriod" />
|
|
46
|
+
<button-tertiary v-if="selection.periods?.length" label="Annuler" class="!px-0" @click="displayPeriodHandler = false" />
|
|
47
|
+
</div>
|
|
48
|
+
<div class="flex flex-col justify-start">
|
|
49
|
+
<input-toggle
|
|
50
|
+
:value="enableToDate || enablePeriodicity"
|
|
51
|
+
:default-value="false"
|
|
52
|
+
:description="'Date de fin différente'"
|
|
53
|
+
:icon="false"
|
|
54
|
+
small
|
|
55
|
+
@change="_toggleToDate"
|
|
56
|
+
/>
|
|
57
|
+
<input-toggle
|
|
58
|
+
:value="enableTime"
|
|
59
|
+
:default-value="false"
|
|
60
|
+
:description="'Inclure des horaires'"
|
|
61
|
+
:icon="false"
|
|
62
|
+
small
|
|
63
|
+
@change="_toggleTime"
|
|
64
|
+
/>
|
|
65
|
+
<input-toggle
|
|
66
|
+
:value="enablePeriodicity"
|
|
67
|
+
:default-value="false"
|
|
68
|
+
:description="'Récurrent'"
|
|
69
|
+
:icon="false"
|
|
70
|
+
small
|
|
71
|
+
@change="_togglePeriodicity"
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
<div v-else class="inline-flex w-full justify-end">
|
|
76
|
+
<button-tertiary label="Ajouter une autre date" class="!px-0" @click="displayPeriodHandler = true" />
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</template>
|
|
80
|
+
|
|
81
|
+
<script setup>
|
|
82
|
+
import { DateTime, Interval } from 'luxon'
|
|
83
|
+
import listTable from '~/components/data/lists/data-lists-table'
|
|
84
|
+
import selectDate from '~/components/forms/selects/select-date'
|
|
85
|
+
import selectEnum from '~/components/forms/selects/select-enum'
|
|
86
|
+
import inputToggle from '~/components/forms/inputs/input-toggle'
|
|
87
|
+
import buttonSecondary from '~/components/elements/buttons/secondary'
|
|
88
|
+
import buttonTertiary from '~/components/elements/buttons/tertiary'
|
|
89
|
+
|
|
90
|
+
// Prepare constants
|
|
91
|
+
const PERIODICITY = [
|
|
92
|
+
{ label: 'Tous les jours', value: 'DAILY' },
|
|
93
|
+
{ label: 'Tous les semaines', value: 'WEEKLY' },
|
|
94
|
+
{ label: 'Tous les mois', value: 'MONTHLY' },
|
|
95
|
+
{ label: 'Chaque année', value: 'YEARLY' },
|
|
96
|
+
]
|
|
97
|
+
const TABLE_HEADERS = [
|
|
98
|
+
{
|
|
99
|
+
property: 'Période',
|
|
100
|
+
component: 'data-summary',
|
|
101
|
+
nestedValue: false
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
title: '',
|
|
105
|
+
component: 'actions',
|
|
106
|
+
componentOptions: {
|
|
107
|
+
options: [
|
|
108
|
+
{ label: 'Supprimer', action: 'remove' }
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
// Prepare props
|
|
115
|
+
const $props = defineProps({
|
|
116
|
+
label: {
|
|
117
|
+
type: String,
|
|
118
|
+
default: ''
|
|
119
|
+
},
|
|
120
|
+
value: {
|
|
121
|
+
type: Array,
|
|
122
|
+
default: () => []
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
// Prepare $emitters
|
|
127
|
+
const $emitters = defineEmits(['keyup.enter', 'change'])
|
|
128
|
+
|
|
129
|
+
// Prepare data
|
|
130
|
+
const selection = ref({})
|
|
131
|
+
const displayPeriodHandler = ref(true)
|
|
132
|
+
const periodFromDate = ref('')
|
|
133
|
+
const periodToDate = ref('')
|
|
134
|
+
const periodPeriodicity = ref('UNIQUE')
|
|
135
|
+
const enableToDate = ref(false)
|
|
136
|
+
const enableTime = ref(false)
|
|
137
|
+
const enablePeriodicity = ref(false)
|
|
138
|
+
|
|
139
|
+
// Prepare computed
|
|
140
|
+
const periodsDataList = computed(
|
|
141
|
+
() => selection.value?.periods?.map(
|
|
142
|
+
({ fromDate, toDate, periodicity }) => ({
|
|
143
|
+
title: Interval.fromISO(`${fromDate}/${toDate}`, { locale: 'fr' }).toFormat('ccc d LLL à H:mm', { locale: 'fr' }),
|
|
144
|
+
description: PERIODICITY.find(({ value }) => value === periodicity)?.label || 'Une seule fois',
|
|
145
|
+
fromDate,
|
|
146
|
+
toDate
|
|
147
|
+
})
|
|
148
|
+
) || []
|
|
149
|
+
)
|
|
150
|
+
// Prepare methods
|
|
151
|
+
const _resetDisplay = () => {
|
|
152
|
+
periodFromDate.value = ''
|
|
153
|
+
periodToDate.value = ''
|
|
154
|
+
periodPeriodicity.value = 'UNIQUE'
|
|
155
|
+
enableToDate.value = false
|
|
156
|
+
enableTime.value = false
|
|
157
|
+
enablePeriodicity.value = false
|
|
158
|
+
displayPeriodHandler.value = false
|
|
159
|
+
}
|
|
160
|
+
const _togglePeriodicity = (value) => {
|
|
161
|
+
enablePeriodicity.value = value
|
|
162
|
+
enableToDate.value = value
|
|
163
|
+
}
|
|
164
|
+
const _toggleToDate = (value) => {
|
|
165
|
+
enableToDate.value = value
|
|
166
|
+
}
|
|
167
|
+
const _toggleTime = (value) => {
|
|
168
|
+
enableTime.value = value
|
|
169
|
+
}
|
|
170
|
+
const _updateCurrentPeriod = (beginOrEnd, data) => {
|
|
171
|
+
if (beginOrEnd === 'begin') {
|
|
172
|
+
periodFromDate.value = data.beginTime
|
|
173
|
+
if (!enableToDate.value) {
|
|
174
|
+
periodToDate.value = data.endTime
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
periodToDate.value = data.date
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const _addPeriod = () => {
|
|
181
|
+
if (!selection.value?.periods) {
|
|
182
|
+
selection.value = { periods: [] }
|
|
183
|
+
}
|
|
184
|
+
// La date AFFICHÉE par défaut compte même sans clic dans le calendrier —
|
|
185
|
+
// le widget hérité enregistrait des périodes VIDES dans ce cas (fromDate '').
|
|
186
|
+
const fromDate = periodFromDate.value ||
|
|
187
|
+
DateTime.now().setZone('Europe/Paris').set({ second: 0, millisecond: 0 }).toISO()
|
|
188
|
+
const toDate = periodToDate.value || fromDate
|
|
189
|
+
selection.value.periods.push({
|
|
190
|
+
fromDate,
|
|
191
|
+
toDate,
|
|
192
|
+
periodicity: periodPeriodicity.value
|
|
193
|
+
})
|
|
194
|
+
$emitters('change', selection.value)
|
|
195
|
+
_resetDisplay()
|
|
196
|
+
}
|
|
197
|
+
const _removePeriod = (val) => {
|
|
198
|
+
const indexToRemove = selection.value.periods?.findIndex(
|
|
199
|
+
({ fromDate, toDate }) => fromDate === val.fromDate && toDate === val.toDate
|
|
200
|
+
)
|
|
201
|
+
if (indexToRemove >= 0) {
|
|
202
|
+
selection.value.periods?.splice(indexToRemove, 1)
|
|
203
|
+
}
|
|
204
|
+
$emitters('change', selection.value)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// prepare watchers
|
|
208
|
+
watch(
|
|
209
|
+
() => $props.value,
|
|
210
|
+
(newValue) => {
|
|
211
|
+
if (newValue?.periods?.length) {
|
|
212
|
+
selection.value = newValue
|
|
213
|
+
displayPeriodHandler.value = !selection.value?.periods?.length
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
{ immediate: true }
|
|
217
|
+
)
|
|
218
|
+
</script>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="relative block w-full">
|
|
3
|
+
<label v-if="label" id="listbox-label" class="block text-2xs font-light tracking-wider text-gray-500 uppercase">
|
|
4
|
+
{{ label }}
|
|
5
|
+
</label>
|
|
6
|
+
<!-- <div class="bg-gray-200 text-gray-500 text-sm rounded-md w-full p-4">
|
|
7
|
+
/!\ Navigation should be based on organization.collections array only<br>
|
|
8
|
+
Collections that does not belong to organization can't be updated or deleted only enabled / disabled<br>
|
|
9
|
+
Let user duplicate an existing collection for this organization only
|
|
10
|
+
</div> -->
|
|
11
|
+
<button-secondary class="my-1" label="Ajouter une étape" @click="_openModalForm('add')" />
|
|
12
|
+
<list-table
|
|
13
|
+
:headers="TABLE_HEADERS"
|
|
14
|
+
:items="selection"
|
|
15
|
+
:actions="['update', 'remove']"
|
|
16
|
+
@update="_openModalForm('update', $event)"
|
|
17
|
+
@remove="_handleStep('remove', $event)"
|
|
18
|
+
/>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup>
|
|
23
|
+
import listTable from '~/components/data/lists/data-lists-table'
|
|
24
|
+
import buttonSecondary from '~/components/elements/buttons/secondary'
|
|
25
|
+
import { useModalStore } from '~/store/layout/modal'
|
|
26
|
+
|
|
27
|
+
const { open } = useModalStore()
|
|
28
|
+
|
|
29
|
+
// prepare constants
|
|
30
|
+
const TABLE_HEADERS = [
|
|
31
|
+
{
|
|
32
|
+
title: 'Nom',
|
|
33
|
+
property: 'name',
|
|
34
|
+
component: 'data-text'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
title: 'Statut',
|
|
38
|
+
property: 'status',
|
|
39
|
+
component: 'data-text'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
title: '',
|
|
43
|
+
component: 'actions'
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
// Prepare props
|
|
48
|
+
const $props = defineProps({
|
|
49
|
+
label: {
|
|
50
|
+
type: String,
|
|
51
|
+
default: ''
|
|
52
|
+
},
|
|
53
|
+
value: {
|
|
54
|
+
type: Array,
|
|
55
|
+
default: () => []
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// Prepare $emitters
|
|
60
|
+
const $emitters = defineEmits(['keyup.enter', 'change'])
|
|
61
|
+
|
|
62
|
+
// Prepare reactive data
|
|
63
|
+
const selection = ref([])
|
|
64
|
+
|
|
65
|
+
// Prepare methods
|
|
66
|
+
const _handleStep = async (action, data = {}) => {
|
|
67
|
+
switch (action) {
|
|
68
|
+
case 'add':
|
|
69
|
+
selection.value.push({ ...data, _id: selection.value.length })
|
|
70
|
+
break
|
|
71
|
+
case 'update':
|
|
72
|
+
selection.value[data._id] = data
|
|
73
|
+
break
|
|
74
|
+
case 'remove':
|
|
75
|
+
selection.value.splice(data._id, 1)
|
|
76
|
+
selection.value = selection.value.map((val, index) => ({ ...val, _id: index }))
|
|
77
|
+
break
|
|
78
|
+
default:
|
|
79
|
+
throw new Error('E_UNHANDLED')
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const _openModalForm = (action, record) => {
|
|
84
|
+
open({
|
|
85
|
+
onSubmit: async (data) => _handleStep(action, { _id: record?._id, ...data }),
|
|
86
|
+
details: {
|
|
87
|
+
title: `${action === 'add' ? 'Créer' : 'Modifier'} une étape`,
|
|
88
|
+
subTitle: 'Vous pouvez détailler votre étape ici.',
|
|
89
|
+
acceptButton: `${action === 'add' ? 'Créer' : 'Modifier'}`,
|
|
90
|
+
closeButton: 'Annuler'
|
|
91
|
+
},
|
|
92
|
+
attributes: [
|
|
93
|
+
{
|
|
94
|
+
property: 'name',
|
|
95
|
+
componentOptions: {
|
|
96
|
+
label: 'Titre de l\'étape',
|
|
97
|
+
name: 'name'
|
|
98
|
+
},
|
|
99
|
+
component: 'input-text'
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
property: 'description',
|
|
103
|
+
componentOptions: {
|
|
104
|
+
label: 'Description de l\'étape',
|
|
105
|
+
name: 'description'
|
|
106
|
+
},
|
|
107
|
+
component: 'input-text-area'
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
property: 'status',
|
|
111
|
+
componentOptions: {
|
|
112
|
+
label: 'Etat de l\'étape',
|
|
113
|
+
name: 'status',
|
|
114
|
+
items: ['Planifié', 'En cours', 'Réalisé'],
|
|
115
|
+
object: false
|
|
116
|
+
},
|
|
117
|
+
component: 'select-enum'
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
property: 'content',
|
|
121
|
+
componentOptions: {
|
|
122
|
+
label: 'Contenu de l\'étape',
|
|
123
|
+
name: 'content'
|
|
124
|
+
},
|
|
125
|
+
component: 'input-wysiwyg'
|
|
126
|
+
}
|
|
127
|
+
],
|
|
128
|
+
record
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// prepare watchers
|
|
133
|
+
watch(
|
|
134
|
+
() => $props.value,
|
|
135
|
+
(newValue) => {
|
|
136
|
+
if (newValue) {
|
|
137
|
+
selection.value = newValue.map((val, index) => ({ _id: index, ...val }))
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
{ immediate: true }
|
|
141
|
+
)
|
|
142
|
+
watch(
|
|
143
|
+
selection,
|
|
144
|
+
(newValue) => $emitters('change', newValue),
|
|
145
|
+
{ immediate: true }
|
|
146
|
+
)
|
|
147
|
+
</script>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="relative block w-full">
|
|
3
|
+
<button-secondary class="my-1" label="Ajouter un token" @click="_openModalForm" />
|
|
4
|
+
<list-table
|
|
5
|
+
:headers="TABLE_HEADERS"
|
|
6
|
+
:items="tokens"
|
|
7
|
+
:actions="['remove']"
|
|
8
|
+
@remove="_removeToken($event)"
|
|
9
|
+
/>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup>
|
|
14
|
+
import listTable from '~/components/data/lists/data-lists-table'
|
|
15
|
+
import buttonSecondary from '~/components/elements/buttons/secondary'
|
|
16
|
+
import { useModalStore } from '~/store/layout/modal'
|
|
17
|
+
|
|
18
|
+
const { Token } = useModels()
|
|
19
|
+
const { open } = useModalStore()
|
|
20
|
+
|
|
21
|
+
// Prepare constants
|
|
22
|
+
const TABLE_HEADERS = [
|
|
23
|
+
{
|
|
24
|
+
title: 'Nom',
|
|
25
|
+
property: 'name',
|
|
26
|
+
component: 'data-text'
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
title: 'Token',
|
|
30
|
+
property: 'token',
|
|
31
|
+
component: 'data-text'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
title: '',
|
|
35
|
+
component: 'actions'
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
// Prepare computed data
|
|
40
|
+
const tokens = computed(() => Token.repo.all())
|
|
41
|
+
|
|
42
|
+
// Prepare methods
|
|
43
|
+
const _createToken = async (data) => {
|
|
44
|
+
await Token.create(data)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const _removeToken = async ({ _id }) => {
|
|
48
|
+
await Token.remove(_id)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const _openModalForm = (action, record) => {
|
|
52
|
+
open({
|
|
53
|
+
onSubmit: _createToken,
|
|
54
|
+
details: {
|
|
55
|
+
title: 'Créer un token',
|
|
56
|
+
subTitle: 'Entrez un nom pour votre token afin de l\'identifier facilement.',
|
|
57
|
+
acceptButton: 'Créer',
|
|
58
|
+
closeButton: 'Annuler'
|
|
59
|
+
},
|
|
60
|
+
attributes: [
|
|
61
|
+
{
|
|
62
|
+
property: 'name',
|
|
63
|
+
componentOptions: {
|
|
64
|
+
placeHolder: 'ex: landing pages, mobile app, ... ',
|
|
65
|
+
name: 'name'
|
|
66
|
+
},
|
|
67
|
+
component: 'input-text'
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Prepare lifecle hooks
|
|
74
|
+
onMounted(async () => {
|
|
75
|
+
if (!tokens.value.length) {
|
|
76
|
+
await Token.list()
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
</script>
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="relative">
|
|
3
|
+
<label
|
|
4
|
+
:for="name"
|
|
5
|
+
class="block text-2xs font-light tracking-wider text-gray-500 uppercase"
|
|
6
|
+
>
|
|
7
|
+
{{ label }}
|
|
8
|
+
</label>
|
|
9
|
+
<div class="inline-flex items-center w-full space-x-2">
|
|
10
|
+
<div class="relative mt-1 rounded-md shadow-sm">
|
|
11
|
+
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
|
12
|
+
<div class="h-5 w-5 mt-1 rounded-full bg-white" :style="`background-color: ${data}`" />
|
|
13
|
+
</div>
|
|
14
|
+
<input
|
|
15
|
+
:id="name"
|
|
16
|
+
v-model="data"
|
|
17
|
+
:name="name"
|
|
18
|
+
:class="{ 'mt-1':!!label }"
|
|
19
|
+
:placeholder="placeHolder"
|
|
20
|
+
:disabled="disabled"
|
|
21
|
+
type="text"
|
|
22
|
+
class="block w-full rounded-md py-2 px-3 pl-10 shadow-xs border border-gray-200 disabled:bg-gray-100 focus:outline-none focus:ring-gray-50 focus:border-black sm:text-sm"
|
|
23
|
+
@keyup.enter="$emit('enter')"
|
|
24
|
+
>
|
|
25
|
+
</div>
|
|
26
|
+
<button
|
|
27
|
+
:class="{ 'mt-1':!!label }"
|
|
28
|
+
class="border border-gray-200 w-auto h-full text-sm py-1.5 px-2 font-medium rounded-md text-gray-900 hover:text-black bg-transparent focus:outline-none focus:ring-gray-50 focus:border-black"
|
|
29
|
+
@click="_toggle"
|
|
30
|
+
>
|
|
31
|
+
<svg
|
|
32
|
+
class="w-6 h-6"
|
|
33
|
+
fill="none"
|
|
34
|
+
stroke="currentColor"
|
|
35
|
+
viewBox="0 0 26 26"
|
|
36
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
37
|
+
>
|
|
38
|
+
<path
|
|
39
|
+
stroke-linecap="round"
|
|
40
|
+
stroke-linejoin="round"
|
|
41
|
+
stroke-width="2"
|
|
42
|
+
d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01"
|
|
43
|
+
/>
|
|
44
|
+
</svg>
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
<transition
|
|
48
|
+
leave-active-class="transition ease-in duration-100"
|
|
49
|
+
leave-from-class="opacity-100"
|
|
50
|
+
leave-to-class="opacity-0"
|
|
51
|
+
>
|
|
52
|
+
<div
|
|
53
|
+
v-if="display"
|
|
54
|
+
ref="inputColorPicker"
|
|
55
|
+
class="border border-gray-300 origin-top-right absolute right-0 top-full mt-2 rounded-md shadow-lg z-20"
|
|
56
|
+
>
|
|
57
|
+
<div class="rounded-md bg-white shadow-xs p-2">
|
|
58
|
+
<div class="flex">
|
|
59
|
+
<div v-for="(color, i) in COLORS" :key="i">
|
|
60
|
+
<div
|
|
61
|
+
v-for="variant in color"
|
|
62
|
+
:key="variant"
|
|
63
|
+
:style="`background-color: ${variant};`"
|
|
64
|
+
class="cursor-pointer w-6 h-6 rounded-full mx-1 my-1"
|
|
65
|
+
@click="_select(variant)"
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</transition>
|
|
72
|
+
</div>
|
|
73
|
+
</template>
|
|
74
|
+
|
|
75
|
+
<script setup>
|
|
76
|
+
import { onClickOutside } from '@vueuse/core'
|
|
77
|
+
|
|
78
|
+
// Prepare constants
|
|
79
|
+
const COLORS = [
|
|
80
|
+
['#E2E8F0', '#64748B', '#1E293B'],
|
|
81
|
+
['#E5E7EB', '#6B7280', '#1F2937'],
|
|
82
|
+
['#FECACA', '#EF4444', '#991B1B'],
|
|
83
|
+
['#FED7AA', '#F97316', '#9A3412'],
|
|
84
|
+
['#FDE68A', '#F59E0B', '#92400E'],
|
|
85
|
+
['#FEF08A', '#EAB308', '#854D0E'],
|
|
86
|
+
['#D9F99D', '#84CC16', '#3F6212'],
|
|
87
|
+
['#BBF7D0', '#22C55E', '#166534'],
|
|
88
|
+
['#A7F3D0', '#10B981', '#065F46'],
|
|
89
|
+
['#99F6E4', '#14B8A6', '#115E59'],
|
|
90
|
+
['#A5F3FC', '#06B6D4', '#155E75'],
|
|
91
|
+
['#BAE6FD', '#0EA5E9', '#075985'],
|
|
92
|
+
['#BFDBFE', '#3B82F6', '#1E40AF'],
|
|
93
|
+
['#C7D2FE', '#6366F1', '#3730A3'],
|
|
94
|
+
['#DDD6FE', '#8B5CF6', '#5B21B6'],
|
|
95
|
+
['#E9D5FF', '#A855F7', '#6B21A8'],
|
|
96
|
+
['#F5D0FE', '#D946EF', '#86198F'],
|
|
97
|
+
['#FBCFE8', '#EC4899', '#9D174D'],
|
|
98
|
+
['#FECDD3', '#F43F5E', '#9F1239']
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
// Prepare $emitters
|
|
102
|
+
const $emitters = defineEmits(['change'])
|
|
103
|
+
|
|
104
|
+
// Prepare props
|
|
105
|
+
const $props = defineProps({
|
|
106
|
+
disabled: {
|
|
107
|
+
type: Boolean,
|
|
108
|
+
default: false
|
|
109
|
+
},
|
|
110
|
+
label: {
|
|
111
|
+
type: String,
|
|
112
|
+
default: ''
|
|
113
|
+
},
|
|
114
|
+
name: {
|
|
115
|
+
type: String,
|
|
116
|
+
default: 'input'
|
|
117
|
+
},
|
|
118
|
+
placeHolder: {
|
|
119
|
+
type: String,
|
|
120
|
+
default: ''
|
|
121
|
+
},
|
|
122
|
+
value: {
|
|
123
|
+
type: String,
|
|
124
|
+
default: ''
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// Prepare reactive data
|
|
129
|
+
const inputColorPicker = ref(null)
|
|
130
|
+
const display = ref(false)
|
|
131
|
+
const data = ref('')
|
|
132
|
+
|
|
133
|
+
// Prepare watchers
|
|
134
|
+
watch(
|
|
135
|
+
() => $props.value,
|
|
136
|
+
(newValue) => {
|
|
137
|
+
data.value = newValue
|
|
138
|
+
},
|
|
139
|
+
{ immediate: true }
|
|
140
|
+
)
|
|
141
|
+
watch(
|
|
142
|
+
data,
|
|
143
|
+
(newValue) => $emitters('change', newValue)
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
// Prepare methods
|
|
147
|
+
const _toggle = () => {
|
|
148
|
+
display.value = !display.value
|
|
149
|
+
}
|
|
150
|
+
const _close = () => {
|
|
151
|
+
display.value = false
|
|
152
|
+
}
|
|
153
|
+
const _select = (color) => {
|
|
154
|
+
data.value = color
|
|
155
|
+
_close()
|
|
156
|
+
}
|
|
157
|
+
// Prepare directives
|
|
158
|
+
onClickOutside(inputColorPicker, _close)
|
|
159
|
+
</script>
|