@live-change/content-frontend 0.2.16 → 0.2.18
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/front/src/components/{PageAdminButtons.vue → ContentAdminButtons.vue} +14 -2
- package/front/src/components/ContentEditor.vue +134 -0
- package/front/src/components/ContentPreview.vue +71 -0
- package/front/src/components/ContentSettings.vue +80 -0
- package/front/src/components/Metadata.vue +0 -4
- package/front/src/components/NotAuthorizedAdminButtons.vue +15 -3
- package/front/src/components/NotFoundAdminButtons.vue +41 -8
- package/front/src/components/Page.vue +4 -52
- package/front/src/components/PageEditor.vue +13 -98
- package/front/src/components/PagePreview.vue +2 -32
- package/front/src/components/UrlContent.vue +81 -0
- package/front/src/router.js +2 -2
- package/index.js +14 -7
- package/package.json +9 -8
- package/server/init.js +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="absolute top-0 right-0 pr-4 max-h-0 flex align-items-center z-5">
|
|
3
|
-
<router-link :to="
|
|
3
|
+
<router-link :to="editorRoute(objectType, object)" class="no-underline">
|
|
4
4
|
<Button icon="pi pi-pencil"
|
|
5
5
|
class="p-button p-button-icon-only p-button-rounded p-button-warning mr-2" />
|
|
6
6
|
</router-link>
|
|
@@ -14,13 +14,25 @@
|
|
|
14
14
|
import Button from 'primevue/button'
|
|
15
15
|
|
|
16
16
|
const props = defineProps({
|
|
17
|
-
|
|
17
|
+
objectType: {
|
|
18
|
+
type: String,
|
|
19
|
+
required: true
|
|
20
|
+
},
|
|
21
|
+
object: {
|
|
18
22
|
type: String,
|
|
19
23
|
required: true
|
|
20
24
|
},
|
|
21
25
|
name: {
|
|
22
26
|
type: String,
|
|
23
27
|
default: ""
|
|
28
|
+
},
|
|
29
|
+
editorRoute: {
|
|
30
|
+
type: Function,
|
|
31
|
+
default: (objectType, object) => {
|
|
32
|
+
const [service, type] = objectType.split('_')
|
|
33
|
+
const prop = type[0].toLowerCase()+type.slice(1)
|
|
34
|
+
return { name: `${service}:${prop}Editor`, params: { [prop+'Id']: object }}
|
|
35
|
+
}
|
|
24
36
|
}
|
|
25
37
|
})
|
|
26
38
|
|
|
@@ -1,9 +1,143 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
|
|
3
|
+
<DocumentEditor :targetType="objectType" :target="object"
|
|
4
|
+
:purpose="purpose" :type="contentType"
|
|
5
|
+
:config="contentConfig || defaultContentConfig"
|
|
6
|
+
:class="className" :style="style"
|
|
7
|
+
v-model:saveState="saveState" v-model:version="version">
|
|
8
|
+
|
|
9
|
+
<template #menuEnd="{}">
|
|
10
|
+
|
|
11
|
+
<router-link :to="previewRoute(objectType, object)" target="_blank"
|
|
12
|
+
class="no-underline">
|
|
13
|
+
<Button icon="pi pi-eye" label="Preview" class="p-button-secondary p-button-sm mr-1 mb-1" />
|
|
14
|
+
</router-link>
|
|
15
|
+
|
|
16
|
+
<div class="p-buttonset mr-1 mb-1 border-round">
|
|
17
|
+
<Button type="button"
|
|
18
|
+
class="p-button p-component p-button-sm p-button-outlined p-button-secondary cursor-auto inline-block"
|
|
19
|
+
:class="{ 'p-disabled': saveState == 'saving' }">
|
|
20
|
+
<span class="pi p-button-icon p-button-icon-left"
|
|
21
|
+
:class="[saveState == 'saving' ? 'pi-sync' : 'pi-hashtag' ]" />
|
|
22
|
+
<span class="p-button-label">{{ ( version ?? 0 ).toFixed().padStart(10, '0') }}</span>
|
|
23
|
+
</Button>
|
|
24
|
+
<Button icon="pi pi-save" label="Publish" class="p-button-success p-button-sm" type="button"
|
|
25
|
+
:disabled="saveState == 'saving'" @click="publish" />
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
</DocumentEditor>
|
|
31
|
+
|
|
32
|
+
<div></div><!-- Because bug in vue -->
|
|
33
|
+
|
|
3
34
|
</template>
|
|
4
35
|
|
|
5
36
|
<script setup>
|
|
6
37
|
|
|
38
|
+
import Button from 'primevue/button'
|
|
39
|
+
|
|
40
|
+
import { DocumentEditor, EditorMenu } from "@live-change/wysiwyg-frontend"
|
|
41
|
+
|
|
42
|
+
import defaultContentConfig from "./contentConfig.js"
|
|
43
|
+
|
|
44
|
+
const props = defineProps({
|
|
45
|
+
objectType: {
|
|
46
|
+
type: String,
|
|
47
|
+
required: true
|
|
48
|
+
},
|
|
49
|
+
object: {
|
|
50
|
+
type: String,
|
|
51
|
+
required: true
|
|
52
|
+
},
|
|
53
|
+
previewRoute: {
|
|
54
|
+
type: Function,
|
|
55
|
+
default: (objectType, object) => {
|
|
56
|
+
const [service, type] = objectType.split('_')
|
|
57
|
+
const prop = type[0].toLowerCase()+type.slice(1)
|
|
58
|
+
return { name: `${service}:${prop}Preview`, params: { [prop]: object }}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
publishTarget: {
|
|
62
|
+
type: String,
|
|
63
|
+
required: true
|
|
64
|
+
},
|
|
65
|
+
contentType: {
|
|
66
|
+
type: String,
|
|
67
|
+
required: true
|
|
68
|
+
},
|
|
69
|
+
purpose: {
|
|
70
|
+
type: String,
|
|
71
|
+
required: true
|
|
72
|
+
},
|
|
73
|
+
contentConfig: {
|
|
74
|
+
type: Object,
|
|
75
|
+
default: () => null
|
|
76
|
+
},
|
|
77
|
+
class: {},
|
|
78
|
+
style: {}
|
|
79
|
+
})
|
|
80
|
+
import { toRefs } from "@vueuse/core"
|
|
81
|
+
const {
|
|
82
|
+
objectType, object, previewRoute, publishTarget, contentType, purpose, contentConfig,
|
|
83
|
+
class: className, style
|
|
84
|
+
} = toRefs(props)
|
|
85
|
+
|
|
86
|
+
import { computed, watch, ref, onMounted, inject } from 'vue'
|
|
87
|
+
|
|
88
|
+
const document = computed(() => `${JSON.stringify(objectType.value)}:${JSON.stringify(object.value)}`)
|
|
89
|
+
|
|
90
|
+
const saveState = ref()
|
|
91
|
+
const version = ref()
|
|
92
|
+
|
|
93
|
+
import { useToast } from 'primevue/usetoast'
|
|
94
|
+
const toast = useToast()
|
|
95
|
+
import { useConfirm } from 'primevue/useconfirm'
|
|
96
|
+
const confirm = useConfirm()
|
|
97
|
+
|
|
98
|
+
const workingZone = inject('workingZone')
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
import { useApi, path, live } from '@live-change/vue3-ssr'
|
|
102
|
+
|
|
103
|
+
const api = useApi()
|
|
104
|
+
|
|
105
|
+
const p = path()
|
|
106
|
+
|
|
107
|
+
const liveCanonicalUrlPath = computed(
|
|
108
|
+
() => p.url.targetOwnedCanonical({ targetType: objectType.value, target: object.value })
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
const [canonicalUrlData] = await Promise.all([
|
|
112
|
+
live(liveCanonicalUrlPath),
|
|
113
|
+
]).catch(e => [null, null, null, null])
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
function publish() {
|
|
117
|
+
const snapshotVersion = version.value
|
|
118
|
+
const typeName = objectType.value.split('_')[1]
|
|
119
|
+
const typeNameLower = typeName[0].toLowerCase()+typeName.slice(1)
|
|
120
|
+
confirm.require({
|
|
121
|
+
target: event.currentTarget,
|
|
122
|
+
message: `Do you want to publish this content version ${snapshotVersion} to ${publishTarget.value}`,
|
|
123
|
+
icon: 'pi pi-info-circle',
|
|
124
|
+
acceptClass: 'p-button-danger',
|
|
125
|
+
accept: async () => {
|
|
126
|
+
api.actions.content.publish({
|
|
127
|
+
objectType: objectType.value, object: object.value, version: snapshotVersion, type: contentType.value
|
|
128
|
+
}).then(() => {
|
|
129
|
+
toast.add({ severity: 'success', summary: 'Published', detail: typeName+' published', life: 3000 })
|
|
130
|
+
}).catch(e => {
|
|
131
|
+
toast.add({ severity: 'error', summary: 'Error', detail: 'Error publishing ' + typeNameLower, life: 3000 })
|
|
132
|
+
})
|
|
133
|
+
},
|
|
134
|
+
reject: () => {
|
|
135
|
+
toast.add({ severity:'error', summary: 'Rejected', detail: 'You have rejected', life: 3000 })
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
}
|
|
140
|
+
|
|
7
141
|
</script>
|
|
8
142
|
|
|
9
143
|
<style scoped>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
|
|
3
|
+
<LimitedAccess :requiredRoles="['writer']" :objectType="objectType" :object="object" hidden>
|
|
4
|
+
<div class="absolute top-0 right-0 pr-4 max-h-0 flex align-items-center z-5">
|
|
5
|
+
<Badge severity="warning" value="This is page preview" class="mr-2" />
|
|
6
|
+
<router-link :to="editorRoute(objectType, object)" class="no-underline">
|
|
7
|
+
<Button icon="pi pi-pencil"
|
|
8
|
+
class="p-button p-button-icon-only p-button-rounded p-button-warning mr-2" />
|
|
9
|
+
</router-link>
|
|
10
|
+
</div>
|
|
11
|
+
<Content :objectType="objectType" :object="object" :style="style" :class="clazz" preview />
|
|
12
|
+
</LimitedAccess>
|
|
13
|
+
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup>
|
|
17
|
+
import Button from "primevue/button"
|
|
18
|
+
import Badge from "primevue/badge"
|
|
19
|
+
|
|
20
|
+
import { ResolveUrl, NotFound } from "@live-change/url-frontend"
|
|
21
|
+
import { LimitedAccess } from "@live-change/access-control-frontend";
|
|
22
|
+
import Content from "./Content.vue"
|
|
23
|
+
|
|
24
|
+
import { computed, watch, ref, onMounted } from 'vue'
|
|
25
|
+
import { toRefs } from "@vueuse/core"
|
|
26
|
+
import { useHost } from "@live-change/frontend-base"
|
|
27
|
+
|
|
28
|
+
const isMounted = ref(false)
|
|
29
|
+
onMounted(() => isMounted.value = true)
|
|
30
|
+
|
|
31
|
+
import { path, live, useApi } from '@live-change/vue3-ssr'
|
|
32
|
+
const api = useApi()
|
|
33
|
+
const p = path()
|
|
34
|
+
|
|
35
|
+
const urlMore = [
|
|
36
|
+
url => p.content.page({ page: url.target })
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
const props = defineProps({
|
|
40
|
+
objectType: {
|
|
41
|
+
type: String,
|
|
42
|
+
required: true
|
|
43
|
+
},
|
|
44
|
+
object: {
|
|
45
|
+
type: String,
|
|
46
|
+
required: true
|
|
47
|
+
},
|
|
48
|
+
class: {
|
|
49
|
+
type: String,
|
|
50
|
+
default: ''
|
|
51
|
+
},
|
|
52
|
+
style: {
|
|
53
|
+
type: String,
|
|
54
|
+
default: ''
|
|
55
|
+
},
|
|
56
|
+
editorRoute: {
|
|
57
|
+
type: Function,
|
|
58
|
+
default: (objectType, object) => {
|
|
59
|
+
const [service, type] = objectType.split('_')
|
|
60
|
+
const prop = type[0].toLowerCase()+type.slice(1)
|
|
61
|
+
return { name: `${service}:${prop}Editor`, params: { [prop+'Id']: object }}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
const { objectType, object, class: clazz, style } = toRefs(props)
|
|
66
|
+
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<style scoped>
|
|
70
|
+
|
|
71
|
+
</style>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Accordion :multiple="true" class="w-full mb-2">
|
|
3
|
+
<AccordionTab>
|
|
4
|
+
<template #header>
|
|
5
|
+
<UrlsInfo :targetType="objectType" :target="object" class="w-full" />
|
|
6
|
+
</template>
|
|
7
|
+
<Urls :key="key" :targetType="objectType" :target="object" />
|
|
8
|
+
</AccordionTab>
|
|
9
|
+
<AccordionTab>
|
|
10
|
+
<template #header>
|
|
11
|
+
<span class="font-bold mr-1">Public access: {{ publicAccessLevel }}</span>
|
|
12
|
+
</template>
|
|
13
|
+
<AccessControl :objectType="objectType" :object="object" />
|
|
14
|
+
</AccordionTab>
|
|
15
|
+
<AccordionTab>
|
|
16
|
+
<template #header>
|
|
17
|
+
<span v-if="metadata" class="font-bold mr-1">Metadata:</span>
|
|
18
|
+
<span v-if="metadata" class="mr-1 font-normal">{{ metadata.title }}</span>
|
|
19
|
+
<span v-else class="font-bold text-red-600">Metadata not set</span>
|
|
20
|
+
</template>
|
|
21
|
+
<MetadataEditor :objectType="objectType" :object="object" :key="key"></MetadataEditor>
|
|
22
|
+
</AccordionTab>
|
|
23
|
+
</Accordion>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script setup>
|
|
27
|
+
|
|
28
|
+
import Accordion from 'primevue/accordion'
|
|
29
|
+
import AccordionTab from 'primevue/accordiontab'
|
|
30
|
+
import Button from 'primevue/button'
|
|
31
|
+
|
|
32
|
+
import { UrlsInfo, Urls, NotFound } from '@live-change/url-frontend'
|
|
33
|
+
import { AccessControl } from '@live-change/access-control-frontend'
|
|
34
|
+
import MetadataEditor from "./MetadataEditor.vue"
|
|
35
|
+
|
|
36
|
+
const props = defineProps({
|
|
37
|
+
objectType: {
|
|
38
|
+
type: String,
|
|
39
|
+
required: true
|
|
40
|
+
},
|
|
41
|
+
object: {
|
|
42
|
+
type: String,
|
|
43
|
+
required: true
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
import { toRefs } from "@vueuse/core"
|
|
48
|
+
import { computed } from 'vue'
|
|
49
|
+
|
|
50
|
+
const { objectType, object } = toRefs(props)
|
|
51
|
+
|
|
52
|
+
const key = computed(() => `${objectType.value}:${object.value}`)
|
|
53
|
+
|
|
54
|
+
import { useApi, path, live } from '@live-change/vue3-ssr'
|
|
55
|
+
const api = useApi()
|
|
56
|
+
const p = path()
|
|
57
|
+
|
|
58
|
+
const livePublicAccessPath = computed(
|
|
59
|
+
() => p.accessControl.objectOwnedPublicAccess({ objectType: objectType.value, object: object.value })
|
|
60
|
+
)
|
|
61
|
+
const liveMetadataPath = computed(
|
|
62
|
+
() => p.content.objectOwnedMetadata({ objectType: objectType.value, object: object.value })
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
const [publicAccessData, metadata] = await Promise.all([
|
|
66
|
+
live(livePublicAccessPath),
|
|
67
|
+
live(liveMetadataPath)
|
|
68
|
+
]).catch(e => [null, null, null, null])
|
|
69
|
+
|
|
70
|
+
const publicAccessLevel = computed(() => {
|
|
71
|
+
if(publicAccessData?.sessionRoles?.includes('reader')) return 'session'
|
|
72
|
+
if(publicAccessData?.userRoles?.includes('reader')) return 'user'
|
|
73
|
+
return 'none'
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<style scoped>
|
|
79
|
+
|
|
80
|
+
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="absolute top-0 right-0 pr-4 max-h-0 flex align-items-center z-5">
|
|
3
|
-
<router-link :to="
|
|
3
|
+
<router-link :to="editorRoute(objectType, object)" class="no-underline">
|
|
4
4
|
<Button icon="pi pi-pencil"
|
|
5
5
|
class="p-button p-button-icon-only p-button-rounded p-button-warning mr-2" />
|
|
6
6
|
</router-link>
|
|
@@ -15,15 +15,27 @@
|
|
|
15
15
|
type: String,
|
|
16
16
|
required: true
|
|
17
17
|
},
|
|
18
|
-
|
|
18
|
+
objectType: {
|
|
19
19
|
type: String,
|
|
20
20
|
required: true
|
|
21
|
+
},
|
|
22
|
+
object: {
|
|
23
|
+
type: String,
|
|
24
|
+
required: true
|
|
25
|
+
},
|
|
26
|
+
editorRoute: {
|
|
27
|
+
type: Function,
|
|
28
|
+
default: (objectType, object) => {
|
|
29
|
+
const [service, type] = objectType.split('_')
|
|
30
|
+
const prop = type[0].toLowerCase()+type.slice(1)
|
|
31
|
+
return { name: `${service}:${prop}Editor`, params: { [prop+'Id']: object }}
|
|
32
|
+
}
|
|
21
33
|
}
|
|
22
34
|
})
|
|
23
35
|
|
|
24
36
|
import { toRefs } from "@vueuse/core"
|
|
25
37
|
|
|
26
|
-
const {
|
|
38
|
+
const { objectType, object, editorRoute } = toRefs(props)
|
|
27
39
|
|
|
28
40
|
</script>
|
|
29
41
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="absolute top-0 right-0 pr-4 max-h-0 flex align-items-center z-5">
|
|
3
3
|
<Button icon="pi pi-plus"
|
|
4
4
|
class="p-button p-button-icon-only p-button-rounded p-button-danger"
|
|
5
|
-
@click="
|
|
5
|
+
@click="createContent" />
|
|
6
6
|
</div>
|
|
7
7
|
</template>
|
|
8
8
|
|
|
@@ -13,15 +13,36 @@
|
|
|
13
13
|
path: {
|
|
14
14
|
type: String,
|
|
15
15
|
required: true
|
|
16
|
+
},
|
|
17
|
+
objectType: {
|
|
18
|
+
type: String,
|
|
19
|
+
required: true
|
|
20
|
+
},
|
|
21
|
+
editorRoute: {
|
|
22
|
+
type: Function,
|
|
23
|
+
default: (objectType, object) => {
|
|
24
|
+
const [service, type] = objectType.split('_')
|
|
25
|
+
const prop = type[0].toLowerCase()+type.slice(1)
|
|
26
|
+
return { name: `${service}:${prop}Editor`, params: { [prop+'Id']: object }}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
createActionName: {
|
|
30
|
+
type: String,
|
|
31
|
+
default: null
|
|
32
|
+
},
|
|
33
|
+
createActionParams: {
|
|
34
|
+
type: Object,
|
|
35
|
+
default: () => {}
|
|
16
36
|
}
|
|
17
37
|
})
|
|
18
38
|
|
|
19
39
|
import { toRefs } from "@vueuse/core"
|
|
40
|
+
import { computed } from "vue"
|
|
20
41
|
|
|
21
42
|
import { useHost } from "@live-change/frontend-base"
|
|
22
43
|
const host = useHost()
|
|
23
44
|
|
|
24
|
-
const { path } = toRefs(props)
|
|
45
|
+
const { path, objectType } = toRefs(props)
|
|
25
46
|
|
|
26
47
|
import { useToast } from 'primevue/usetoast'
|
|
27
48
|
const toast = useToast()
|
|
@@ -31,20 +52,32 @@
|
|
|
31
52
|
import { useRouter } from 'vue-router'
|
|
32
53
|
const router = useRouter()
|
|
33
54
|
|
|
34
|
-
|
|
55
|
+
const serviceName = computed(() => {
|
|
56
|
+
const [service, type] = objectType.value.split('_')
|
|
57
|
+
return service
|
|
58
|
+
})
|
|
59
|
+
const typeName = computed(() => {
|
|
60
|
+
const [service, type] = objectType.value.split('_')
|
|
61
|
+
return type
|
|
62
|
+
})
|
|
63
|
+
const typeNameLower = computed(() => typeName.value[0].toLowerCase()+typeName.value.slice(1))
|
|
64
|
+
|
|
65
|
+
function createContent() {
|
|
35
66
|
confirm.require({
|
|
36
67
|
target: event.currentTarget,
|
|
37
|
-
message: `Do you want to create
|
|
68
|
+
message: `Do you want to create ${typeNameLower.value} at ${host}/${path.value} ?`,
|
|
38
69
|
icon: 'pi pi-info-circle',
|
|
39
70
|
acceptClass: 'p-button-danger',
|
|
40
71
|
accept: async () => {
|
|
41
|
-
const
|
|
72
|
+
const contentId = await api.actions[serviceName.value][props.createActionName || `create${typeName.value}`]({
|
|
73
|
+
...props.createActionParams
|
|
74
|
+
})
|
|
42
75
|
await api.actions.url.takeUrl({
|
|
43
|
-
target:
|
|
76
|
+
target: contentId, targetType: objectType.value, path: path.value, domain: host, redirect: false
|
|
44
77
|
})
|
|
45
|
-
toast.add({ severity:'success', summary:
|
|
78
|
+
toast.add({ severity:'success', summary: `${typeName} created`, life: 1500 })
|
|
46
79
|
setTimeout(() => {
|
|
47
|
-
router.push(
|
|
80
|
+
router.push(props.editorRoute(objectType.value, contentId))
|
|
48
81
|
}, 200)
|
|
49
82
|
},
|
|
50
83
|
reject: () => {
|
|
@@ -1,67 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<template #default="{ target, style, class: clazz }">
|
|
4
|
-
<Metadata objectType="content_Page" :object="target" />
|
|
5
|
-
<LimitedAccess :requiredRoles="['writer']" objectType="content_Page" :object="target" hidden>
|
|
6
|
-
<PageAdminButtons :page="target" :style="style" :class="clazz" :name="urlPath.value" />
|
|
7
|
-
</LimitedAccess>
|
|
8
|
-
<Content objectType="content_Page" :object="target" class="w-full" />
|
|
9
|
-
</template>
|
|
10
|
-
<template #notFound="{ path, style, class: clazz }">
|
|
11
|
-
<NotFoundAdminButtons v-if="canCreatePage" :path="urlPath" :style="style" :class="clazz" />
|
|
12
|
-
<NotFound :style="style" :class="clazz" />
|
|
13
|
-
</template>
|
|
14
|
-
<template #notAuthorized="{ path, style, class: clazz, target, access }">
|
|
15
|
-
<NotAuthorizedAdminButtons v-if="(access?.roles ?? []).includes('writer')"
|
|
16
|
-
:path="urlPath" :style="style" :class="clazz" :target="target" />
|
|
17
|
-
<NotAuthorized :style="style" :class="clazz" />
|
|
18
|
-
</template>
|
|
19
|
-
</ResolveUrl>
|
|
2
|
+
<UrlContent objectType="content_Page" :path="urlPath" :class="clazz" :style="style" />
|
|
20
3
|
</template>
|
|
21
4
|
|
|
22
5
|
<script setup>
|
|
23
|
-
import Button from "primevue/button"
|
|
24
|
-
import PageAdminButtons from "./PageAdminButtons.vue"
|
|
25
|
-
import NotFoundAdminButtons from "./NotFoundAdminButtons.vue"
|
|
26
|
-
import NotAuthorizedAdminButtons from "./NotAuthorizedAdminButtons.vue"
|
|
27
6
|
|
|
28
|
-
import { ResolveUrl, NotFound, NotAuthorized } from "@live-change/url-frontend"
|
|
29
|
-
import { LimitedAccess } from "@live-change/access-control-frontend";
|
|
30
|
-
import Content from "./Content.vue"
|
|
31
|
-
import Metadata from "./Metadata.vue"
|
|
32
|
-
|
|
33
|
-
import { computed, watch, ref, onMounted } from 'vue'
|
|
34
7
|
import { toRefs } from "@vueuse/core"
|
|
35
|
-
|
|
36
|
-
const isMounted = ref(false)
|
|
37
|
-
onMounted(() => isMounted.value = true)
|
|
38
|
-
|
|
39
|
-
import { path, live, useApi } from '@live-change/vue3-ssr'
|
|
40
|
-
const api = useApi()
|
|
41
|
-
const p = path()
|
|
42
|
-
|
|
43
|
-
const urlMore = [
|
|
44
|
-
url => p.content.page({ page: url.target }),
|
|
45
|
-
url => p.content.content({ objectType: 'content_Page', object: url.target }),
|
|
46
|
-
url => p.content.objectOwnedMetadata({ objectType: 'content_Page', object: url.target }),
|
|
47
|
-
url => p.url.targetOwnedCanonical({ targetType: 'content_Page', target: url.target })
|
|
48
|
-
]
|
|
49
|
-
|
|
50
|
-
const canCreatePage = computed(() => api.client.value.roles.includes('writer'))
|
|
8
|
+
import UrlContent from "./UrlContent.vue";
|
|
51
9
|
|
|
52
10
|
const props = defineProps({
|
|
53
11
|
path: {
|
|
54
12
|
type: String,
|
|
55
13
|
required: true
|
|
56
14
|
},
|
|
57
|
-
class: {
|
|
58
|
-
|
|
59
|
-
default: ''
|
|
60
|
-
},
|
|
61
|
-
style: {
|
|
62
|
-
type: String,
|
|
63
|
-
default: ''
|
|
64
|
-
}
|
|
15
|
+
class: { },
|
|
16
|
+
style: { }
|
|
65
17
|
})
|
|
66
18
|
const { path: urlPath, class: clazz, style } = toRefs(props)
|
|
67
19
|
|
|
@@ -1,65 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<AccordionTab>
|
|
4
|
-
<template #header>
|
|
5
|
-
<UrlsInfo targetType="content_Page" :target="pageId" class="w-full" />
|
|
6
|
-
</template>
|
|
7
|
-
<Urls :key="pageId" targetType="content_Page" :target="pageId" />
|
|
8
|
-
</AccordionTab>
|
|
9
|
-
<AccordionTab>
|
|
10
|
-
<template #header>
|
|
11
|
-
<span class="font-bold mr-1">Public access: {{ publicAccessLevel }}</span>
|
|
12
|
-
</template>
|
|
13
|
-
<AccessControl objectType="content_Page" :object="pageId" />
|
|
14
|
-
</AccordionTab>
|
|
15
|
-
<AccordionTab>
|
|
16
|
-
<template #header>
|
|
17
|
-
<span v-if="metadata" class="font-bold mr-1">Metadata:</span>
|
|
18
|
-
<span v-if="metadata" class="mr-1 font-normal">{{ metadata.title }}</span>
|
|
19
|
-
<span v-else class="font-bold text-red-600">Metadata not set</span>
|
|
20
|
-
</template>
|
|
21
|
-
<MetadataEditor objectType="content_Page" :object="pageId" :key="pageId"></MetadataEditor>
|
|
22
|
-
</AccordionTab>
|
|
23
|
-
</Accordion>
|
|
24
|
-
|
|
25
|
-
<DocumentEditor v-if="pageData" targetType="content_Page" :target="pageId" purpose="page"
|
|
26
|
-
:config="contentConfig" type="page" v-model:saveState="saveState" v-model:version="version">
|
|
27
|
-
<template #menuEnd="{}">
|
|
28
|
-
|
|
29
|
-
<router-link :to="{name: 'content:pagePreview', params: { page: pageId }}" target="_blank"
|
|
30
|
-
class="no-underline">
|
|
31
|
-
<Button icon="pi pi-eye" label="Preview" class="p-button-secondary p-button-sm mr-1 mb-1" />
|
|
32
|
-
</router-link>
|
|
33
|
-
|
|
34
|
-
<div class="p-buttonset mr-1 mb-1 border-round">
|
|
35
|
-
<button type="button"
|
|
36
|
-
class="p-button p-component p-button-sm p-button-outlined p-button-secondary cursor-auto inline-block"
|
|
37
|
-
:class="{ 'p-disabled': saveState == 'saving' }">
|
|
38
|
-
<span class="pi p-button-icon p-button-icon-left"
|
|
39
|
-
:class="[saveState == 'saving' ? 'pi-sync' : 'pi-hashtag' ]" />
|
|
40
|
-
<span class="p-button-label">{{ ( version ?? 0 ).toFixed().padStart(10, '0') }}</span>
|
|
41
|
-
</button>
|
|
42
|
-
<Button icon="pi pi-save" label="Publish" class="p-button-success p-button-sm" type="button"
|
|
43
|
-
:disabled="saveState == 'saving'" @click="publish" />
|
|
44
|
-
</div>
|
|
45
|
-
|
|
46
|
-
</template>
|
|
47
|
-
</DocumentEditor>
|
|
2
|
+
<!-- <ContentSettings v-if="pageData" objectType="content_Page" :object="pageId" />-->
|
|
48
3
|
|
|
4
|
+
<ContentEditor v-if="pageData"
|
|
5
|
+
objectType="content_Page" :object="pageId"
|
|
6
|
+
:publishTarget="`page `+`${canonicalUrlData.domain ?? '*'}/${canonicalUrlData.path}`"
|
|
7
|
+
contentType="page" purpose="page" />
|
|
49
8
|
<NotFound v-if="pageData === null" />
|
|
9
|
+
|
|
50
10
|
</template>
|
|
51
11
|
|
|
52
12
|
<script setup>
|
|
13
|
+
import { NotFound } from "@live-change/url-frontend";
|
|
53
14
|
|
|
54
|
-
import
|
|
55
|
-
import
|
|
56
|
-
import Button from 'primevue/button'
|
|
57
|
-
|
|
58
|
-
import { UrlsInfo, Urls, NotFound } from '@live-change/url-frontend'
|
|
59
|
-
import { DocumentEditor, EditorMenu } from "@live-change/wysiwyg-frontend"
|
|
60
|
-
import { AccessControl } from '@live-change/access-control-frontend'
|
|
61
|
-
import MetadataEditor from "./MetadataEditor.vue"
|
|
62
|
-
import contentConfig from "./contentConfig.js";
|
|
15
|
+
import ContentSettings from "./ContentSettings.vue"
|
|
16
|
+
import ContentEditor from "./ContentEditor.vue"
|
|
63
17
|
|
|
64
18
|
import { computed, watch, ref, onMounted, inject } from 'vue'
|
|
65
19
|
import { toRefs } from "@vueuse/core"
|
|
@@ -77,22 +31,8 @@
|
|
|
77
31
|
})
|
|
78
32
|
const { pageId, class: clazz, style } = toRefs(props)
|
|
79
33
|
|
|
80
|
-
const document = computed(() => `${JSON.stringify('content_Page')}:${JSON.stringify(pageId.value)}`)
|
|
81
|
-
|
|
82
|
-
const saveState = ref()
|
|
83
|
-
const version = ref()
|
|
84
|
-
|
|
85
|
-
import { useToast } from 'primevue/usetoast'
|
|
86
|
-
const toast = useToast()
|
|
87
|
-
import { useConfirm } from 'primevue/useconfirm'
|
|
88
|
-
const confirm = useConfirm()
|
|
89
|
-
|
|
90
|
-
const workingZone = inject('workingZone')
|
|
91
|
-
|
|
92
34
|
import { useApi, path, live } from '@live-change/vue3-ssr'
|
|
93
|
-
|
|
94
35
|
const api = useApi()
|
|
95
|
-
|
|
96
36
|
const p = path()
|
|
97
37
|
|
|
98
38
|
const livePagePath = computed(
|
|
@@ -115,36 +55,11 @@
|
|
|
115
55
|
live(liveMetadataPath)
|
|
116
56
|
]).catch(e => [null, null, null, null])
|
|
117
57
|
|
|
118
|
-
const publicAccessLevel = computed(() => {
|
|
119
|
-
if(publicAccessData?.sessionRoles?.includes('reader')) return 'session'
|
|
120
|
-
if(publicAccessData?.userRoles?.includes('reader')) return 'user'
|
|
121
|
-
return 'none'
|
|
122
|
-
})
|
|
123
58
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
target: event.currentTarget,
|
|
129
|
-
message: `Do you want to publish this content version ${snapshotVersion} to page `+
|
|
130
|
-
`${canonicalUrlData.value.domain ?? '*'}/${canonicalUrlData.value.path}?`,
|
|
131
|
-
icon: 'pi pi-info-circle',
|
|
132
|
-
acceptClass: 'p-button-danger',
|
|
133
|
-
accept: async () => {
|
|
134
|
-
api.actions.content.publish({
|
|
135
|
-
objectType: 'content_Page', object: pageId.value, version: snapshotVersion, type: 'page'
|
|
136
|
-
}).then(() => {
|
|
137
|
-
toast.add({ severity: 'success', summary: 'Published', detail: 'Page published', life: 3000 })
|
|
138
|
-
}).catch(e => {
|
|
139
|
-
toast.add({ severity: 'error', summary: 'Error', detail: 'Error publishing page', life: 3000 })
|
|
140
|
-
})
|
|
141
|
-
},
|
|
142
|
-
reject: () => {
|
|
143
|
-
toast.add({ severity:'error', summary: 'Rejected', detail: 'You have rejected', life: 3000 })
|
|
144
|
-
}
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
}
|
|
59
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms))
|
|
60
|
+
|
|
61
|
+
console.log("WAITING!")
|
|
62
|
+
await sleep(5000)
|
|
148
63
|
|
|
149
64
|
</script>
|
|
150
65
|
|
|
@@ -1,40 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
<LimitedAccess :requiredRoles="['writer']" objectType="content_Page" :object="pageId" hidden>
|
|
4
|
-
<div class="absolute top-0 right-0 pr-4 max-h-0 flex align-items-center z-5">
|
|
5
|
-
<Badge severity="warning" value="This is page preview" class="mr-2" />
|
|
6
|
-
<router-link :to="{ name: 'content:pageEditor', params: { pageId } }" class="no-underline">
|
|
7
|
-
<Button icon="pi pi-pencil"
|
|
8
|
-
class="p-button p-button-icon-only p-button-rounded p-button-warning mr-2" />
|
|
9
|
-
</router-link>
|
|
10
|
-
</div>
|
|
11
|
-
<Content objectType="content_Page" :object="pageId" :style="style" :class="clazz" preview />
|
|
12
|
-
</LimitedAccess>
|
|
13
|
-
|
|
2
|
+
<ContentPreview objectType="content_Page" :object="pageId" :style="style" :class="clazz" />
|
|
14
3
|
</template>
|
|
15
4
|
|
|
16
5
|
<script setup>
|
|
17
|
-
import Button from "primevue/button"
|
|
18
|
-
import Badge from "primevue/badge"
|
|
19
6
|
|
|
20
|
-
import
|
|
21
|
-
import { LimitedAccess } from "@live-change/access-control-frontend";
|
|
22
|
-
import Content from "./Content.vue"
|
|
23
|
-
|
|
24
|
-
import { computed, watch, ref, onMounted } from 'vue'
|
|
7
|
+
import ContentPreview from "./ContentPreview.vue";
|
|
25
8
|
import { toRefs } from "@vueuse/core"
|
|
26
|
-
import { useHost } from "@live-change/frontend-base"
|
|
27
|
-
|
|
28
|
-
const isMounted = ref(false)
|
|
29
|
-
onMounted(() => isMounted.value = true)
|
|
30
|
-
|
|
31
|
-
import { path, live, useApi } from '@live-change/vue3-ssr'
|
|
32
|
-
const api = useApi()
|
|
33
|
-
const p = path()
|
|
34
|
-
|
|
35
|
-
const urlMore = [
|
|
36
|
-
url => p.content.page({ page: url.target })
|
|
37
|
-
]
|
|
38
9
|
|
|
39
10
|
const props = defineProps({
|
|
40
11
|
pageId: {
|
|
@@ -51,7 +22,6 @@
|
|
|
51
22
|
}
|
|
52
23
|
})
|
|
53
24
|
const { pageId, class: clazz, style } = toRefs(props)
|
|
54
|
-
|
|
55
25
|
</script>
|
|
56
26
|
|
|
57
27
|
<style scoped>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ResolveUrl :targetType="objectType" :path="urlPath" :fetchMore="urlMore">
|
|
3
|
+
<template #default="{ target, style, class: clazz }">
|
|
4
|
+
<Metadata :objectType="objectType" :object="target" />
|
|
5
|
+
<LimitedAccess :requiredRoles="['writer']" :objectType="objectType" :object="target" hidden>
|
|
6
|
+
<ContentAdminButtons :objectType="objectType" :object="target" :style="style" :class="clazz"
|
|
7
|
+
:name="urlPath.value" :editorRoute="editorRoute" />
|
|
8
|
+
</LimitedAccess>
|
|
9
|
+
<Content :objectType="objectType" :object="target" class="w-full" :style="style" :class="clazz" />
|
|
10
|
+
</template>
|
|
11
|
+
<template #notFound="{ path, style, class: clazz }">
|
|
12
|
+
<NotFoundAdminButtons v-if="canCreateContent" :path="urlPath"
|
|
13
|
+
:objectType="objectType" :editorRoute="editorRoute"
|
|
14
|
+
:style="style" :class="clazz" />
|
|
15
|
+
<NotFound :style="style" :class="clazz" />
|
|
16
|
+
</template>
|
|
17
|
+
<template #notAuthorized="{ path, style, class: clazz, target, access }">
|
|
18
|
+
<NotAuthorizedAdminButtons v-if="(access?.roles ?? []).includes('writer')"
|
|
19
|
+
:path="urlPath" :style="style" :class="clazz" :object="target"
|
|
20
|
+
:objectType="objectType" :editorRoute="editorRoute"/>
|
|
21
|
+
<NotAuthorized :style="style" :class="clazz" />
|
|
22
|
+
</template>
|
|
23
|
+
</ResolveUrl>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script setup>
|
|
27
|
+
import Button from "primevue/button"
|
|
28
|
+
import ContentAdminButtons from "./ContentAdminButtons.vue"
|
|
29
|
+
import NotFoundAdminButtons from "./NotFoundAdminButtons.vue"
|
|
30
|
+
import NotAuthorizedAdminButtons from "./NotAuthorizedAdminButtons.vue"
|
|
31
|
+
|
|
32
|
+
import { ResolveUrl, NotFound, NotAuthorized } from "@live-change/url-frontend"
|
|
33
|
+
import { LimitedAccess } from "@live-change/access-control-frontend";
|
|
34
|
+
import Content from "./Content.vue"
|
|
35
|
+
import Metadata from "./Metadata.vue"
|
|
36
|
+
|
|
37
|
+
import { computed, watch, ref, onMounted } from 'vue'
|
|
38
|
+
import { toRefs } from "@vueuse/core"
|
|
39
|
+
|
|
40
|
+
const isMounted = ref(false)
|
|
41
|
+
onMounted(() => isMounted.value = true)
|
|
42
|
+
|
|
43
|
+
import { path, live, useApi } from '@live-change/vue3-ssr'
|
|
44
|
+
const api = useApi()
|
|
45
|
+
const p = path()
|
|
46
|
+
|
|
47
|
+
const props = defineProps({
|
|
48
|
+
objectType: {
|
|
49
|
+
type: String,
|
|
50
|
+
required: true
|
|
51
|
+
},
|
|
52
|
+
path: {
|
|
53
|
+
type: String,
|
|
54
|
+
required: true
|
|
55
|
+
},
|
|
56
|
+
editorRoute: {
|
|
57
|
+
type: Function,
|
|
58
|
+
default: (objectType, object) => {
|
|
59
|
+
const [service, type] = objectType.split('_')
|
|
60
|
+
const prop = type[0].toLowerCase()+type.slice(1)
|
|
61
|
+
return { name: `${service}:${prop}Editor`, params: { [prop+'Id']: object }}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
class: {},
|
|
65
|
+
style: {}
|
|
66
|
+
})
|
|
67
|
+
const { objectType, path: urlPath, class: clazz, style } = toRefs(props)
|
|
68
|
+
|
|
69
|
+
const urlMore = computed(() => [
|
|
70
|
+
url => p.content.content({ objectType: objectType.value, object: url.target }),
|
|
71
|
+
url => p.content.objectOwnedMetadata({ objectType: objectType.value, object: url.target }),
|
|
72
|
+
url => p.url.targetOwnedCanonical({ targetType: objectType.value, target: url.target })
|
|
73
|
+
])
|
|
74
|
+
|
|
75
|
+
const canCreateContent = computed(() => api.client.value.roles.includes('writer'))
|
|
76
|
+
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<style scoped>
|
|
80
|
+
|
|
81
|
+
</style>
|
package/front/src/router.js
CHANGED
|
@@ -8,7 +8,7 @@ import { dbAdminRoutes } from "@live-change/db-admin"
|
|
|
8
8
|
import { userRoutes } from "@live-change/user-frontend";
|
|
9
9
|
import { catchAllPagesRoute, contentEditRoutes, pagesSitemap } from "./components/routes";
|
|
10
10
|
|
|
11
|
-
export function
|
|
11
|
+
export function contentRoutes(config = {}) {
|
|
12
12
|
const { prefix = '/', route = (r) => r } = config
|
|
13
13
|
return [
|
|
14
14
|
...userRoutes({ ...config, prefix: prefix + 'user/' }),
|
|
@@ -35,7 +35,7 @@ export async function sitemap(route, api) {
|
|
|
35
35
|
export function createRouter(app, config) {
|
|
36
36
|
const router = _createRouter({
|
|
37
37
|
history: import.meta.env.SSR ? createMemoryHistory() : createWebHistory(),
|
|
38
|
-
routes:
|
|
38
|
+
routes: contentRoutes(config)
|
|
39
39
|
})
|
|
40
40
|
return router
|
|
41
41
|
}
|
package/index.js
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
import Content from './front/src/components/Content.vue'
|
|
2
|
-
import ContentEditor from './front/src/components/ContentEditor.vue'
|
|
3
1
|
import Metadata from './front/src/components/Metadata.vue'
|
|
4
2
|
import MetadataEditor from './front/src/components/MetadataEditor.vue'
|
|
3
|
+
import Content from './front/src/components/Content.vue'
|
|
4
|
+
import ContentEditor from './front/src/components/ContentEditor.vue'
|
|
5
|
+
import ContentPreview from './front/src/components/ContentPreview.vue'
|
|
6
|
+
import ContentSettings from './front/src/components/ContentSettings.vue'
|
|
7
|
+
|
|
8
|
+
export { Metadata, MetadataEditor, Content, ContentEditor, ContentPreview, ContentSettings }
|
|
9
|
+
|
|
5
10
|
import NotFoundAdminButtons from './front/src/components/NotFoundAdminButtons.vue'
|
|
11
|
+
import NotAuthorizedAdminButtons from "./front/src/components/NotAuthorizedAdminButtons.vue"
|
|
12
|
+
import ContentAdminButtons from './front/src/components/ContentAdminButtons.vue'
|
|
13
|
+
import UrlContent from './front/src/components/UrlContent.vue'
|
|
14
|
+
|
|
15
|
+
export { NotFoundAdminButtons, NotAuthorizedAdminButtons, ContentAdminButtons, UrlContent }
|
|
16
|
+
|
|
6
17
|
import Page from './front/src/components/Page.vue'
|
|
7
18
|
import PageEditor from './front/src/components/PageEditor.vue'
|
|
8
19
|
import PagePreview from './front/src/components/PagePreview.vue'
|
|
9
|
-
import PageAdminButtons from './front/src/components/PageAdminButtons.vue'
|
|
10
20
|
|
|
11
|
-
export {
|
|
12
|
-
Content, ContentEditor, Metadata, MetadataEditor, NotFoundAdminButtons, Page, PageEditor, PagePreview,
|
|
13
|
-
PageAdminButtons
|
|
14
|
-
}
|
|
21
|
+
export { Page, PageEditor, PagePreview }
|
|
15
22
|
|
|
16
23
|
import { catchAllPagesRoute, contentEditRoutes, pagesSitemap } from "./front/src/components/routes"
|
|
17
24
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-change/content-frontend",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.18",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"memDev": "lcli memDev --enableSessions --initScript ./init.js --dbAccess",
|
|
6
6
|
"localDevInit": "rm tmp.db; lcli localDev --enableSessions --initScript ./init.js",
|
|
@@ -21,13 +21,14 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@fortawesome/fontawesome-free": "^6.2.0",
|
|
24
|
-
"@live-change/cli": "0.7.
|
|
24
|
+
"@live-change/cli": "0.7.6",
|
|
25
25
|
"@live-change/dao": "0.5.8",
|
|
26
26
|
"@live-change/dao-vue3": "0.5.8",
|
|
27
27
|
"@live-change/dao-websocket": "0.5.8",
|
|
28
|
-
"@live-change/framework": "0.7.
|
|
29
|
-
"@live-change/image-service": "0.3.
|
|
30
|
-
"@live-change/session-service": "0.3.
|
|
28
|
+
"@live-change/framework": "0.7.6",
|
|
29
|
+
"@live-change/image-service": "0.3.8",
|
|
30
|
+
"@live-change/session-service": "0.3.8",
|
|
31
|
+
"@live-change/url-frontend": "^0.2.18",
|
|
31
32
|
"@live-change/vue3-components": "0.2.16",
|
|
32
33
|
"@live-change/vue3-ssr": "0.2.16",
|
|
33
34
|
"@vueuse/core": "^9.1.0",
|
|
@@ -48,10 +49,10 @@
|
|
|
48
49
|
"v-shared-element": "3.1.0",
|
|
49
50
|
"vue-meta": "^3.0.0-alpha.9",
|
|
50
51
|
"vue-router": "^4.1.3",
|
|
51
|
-
"vue3-scroll-border": "0.1.
|
|
52
|
+
"vue3-scroll-border": "0.1.4"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
|
-
"@live-change/codeceptjs-helper": "0.7.
|
|
55
|
+
"@live-change/codeceptjs-helper": "0.7.6",
|
|
55
56
|
"@wdio/selenium-standalone-service": "^7.20.8",
|
|
56
57
|
"codeceptjs": "^3.3.4",
|
|
57
58
|
"generate-password": "1.7.0",
|
|
@@ -63,5 +64,5 @@
|
|
|
63
64
|
"author": "",
|
|
64
65
|
"license": "ISC",
|
|
65
66
|
"description": "",
|
|
66
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "f36a88549c576f43ecc56f358a4fafa486454618"
|
|
67
68
|
}
|