project-booster-vue 8.129.0 → 8.131.1
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 +1 -7
- package/package.json +1 -2
- package/src/components/consent/PbConsent.vue +135 -0
- package/src/components/projects/project-hub/PbProjectHub-Features-Consent.stories.mdx +123 -0
- package/src/components/projects/project-hub/PbProjectHub.vue +14 -0
- package/src/services/api/consentApi.js +33 -0
- package/src/services/api/mocks/consentMock.js +25 -0
- package/src/stores/modules/consentStore.js +68 -0
- package/src/stores/store.js +2 -0
- package/dist/demo.html +0 -1
- package/dist/project-booster-vue.common.js +0 -1927
- package/dist/project-booster-vue.common.js.map +0 -1
- package/dist/project-booster-vue.umd.js +0 -1946
- package/dist/project-booster-vue.umd.js.map +0 -1
- package/dist/project-booster-vue.umd.min.js +0 -2
- package/dist/project-booster-vue.umd.min.js.map +0 -1
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "project-booster-vue",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.131.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test:unit": "vue-cli-service test:unit --forceExit --detectOpenHandles",
|
|
7
|
-
"build:bundle": "vue-cli-service build --target lib --name project-booster-vue ./src/index.js",
|
|
8
7
|
"kozikaza-tokens-build": "env PB_THEME=kozikaza mozaic-tokens-build",
|
|
9
8
|
"lint:js": "vue-cli-service lint --no-fix -- ./",
|
|
10
9
|
"lint:js:fix": "vue-cli-service lint --fix -- ./",
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<m-flex direction="column" full-width class="pb-consent">
|
|
3
|
+
<span class="pb-consent__label">
|
|
4
|
+
Faites le plein d’inspirations et de promotions pour votre projet chaque semaine en vous abonnant à notre
|
|
5
|
+
newsletter
|
|
6
|
+
</span>
|
|
7
|
+
<m-flex
|
|
8
|
+
v-if="isMailConsentUpdating"
|
|
9
|
+
class="pb-consent__loading"
|
|
10
|
+
direction="column"
|
|
11
|
+
align-items="center"
|
|
12
|
+
justify-content="center"
|
|
13
|
+
align-content="center"
|
|
14
|
+
>
|
|
15
|
+
<pb-animable-loader class="pb-consent__loading-loader" />
|
|
16
|
+
<div class="pb-consent__loading-label">Chargement...</div>
|
|
17
|
+
</m-flex>
|
|
18
|
+
<m-flex v-else class="pb-consent__actions">
|
|
19
|
+
<m-button
|
|
20
|
+
class="pb-consent__actions--button"
|
|
21
|
+
label="S'abonner à la newsletter"
|
|
22
|
+
theme="bordered"
|
|
23
|
+
@click="handleUpdateConsent"
|
|
24
|
+
v-if="!errorUpdateMailConsent && !isMailConsentUpdated"
|
|
25
|
+
/>
|
|
26
|
+
<m-notification
|
|
27
|
+
v-if="isMailConsentUpdated"
|
|
28
|
+
class="pb-consent__actions--notification"
|
|
29
|
+
type="success"
|
|
30
|
+
text="Merci, votre inscription à la newsletter est confirmée"
|
|
31
|
+
/>
|
|
32
|
+
<m-notification
|
|
33
|
+
v-if="errorUpdateMailConsent"
|
|
34
|
+
class="pb-consent__actions--notification"
|
|
35
|
+
type="warning"
|
|
36
|
+
text="Une erreur est survenue"
|
|
37
|
+
link-label="Merci de réessayer"
|
|
38
|
+
@link-click="handleRetry"
|
|
39
|
+
/>
|
|
40
|
+
</m-flex>
|
|
41
|
+
</m-flex>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<script>
|
|
45
|
+
import MFlex from '../mozaic/flex/MFlex';
|
|
46
|
+
import MButton from '../mozaic/buttons/MButton';
|
|
47
|
+
import MNotification from '../mozaic/notifications/MNotification';
|
|
48
|
+
import PbAnimableLoader from '../loader/PbAnimableLoader';
|
|
49
|
+
import { useStore } from 'vuex';
|
|
50
|
+
import { computed, onMounted } from 'vue';
|
|
51
|
+
|
|
52
|
+
export default {
|
|
53
|
+
name: 'PbConsent',
|
|
54
|
+
|
|
55
|
+
components: {
|
|
56
|
+
MFlex,
|
|
57
|
+
MButton,
|
|
58
|
+
MNotification,
|
|
59
|
+
PbAnimableLoader,
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
setup() {
|
|
63
|
+
const store = useStore();
|
|
64
|
+
const isMailConsentUpdating = computed(() => store.getters['consent/isMailConsentUpdating']);
|
|
65
|
+
const isMailConsentUpdated = computed(() => store.getters['consent/isMailConsentUpdated']);
|
|
66
|
+
const errorUpdateMailConsent = computed(() => store.getters['consent/getErrorUpdateMailConsent']);
|
|
67
|
+
|
|
68
|
+
const handleUpdateConsent = () => {
|
|
69
|
+
store.dispatch('sendEventToBus', {
|
|
70
|
+
code: 'UPDATE-MAIL-CONSENT',
|
|
71
|
+
payload: {},
|
|
72
|
+
});
|
|
73
|
+
store.dispatch('consent/updateMailConsent');
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const handleRetry = () => {
|
|
77
|
+
store.dispatch('sendEventToBus', {
|
|
78
|
+
code: 'RETRY-UPDATE-MAIL-CONSENT',
|
|
79
|
+
payload: {},
|
|
80
|
+
});
|
|
81
|
+
store.dispatch('consent/updateMailConsent');
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
onMounted(() => {
|
|
85
|
+
store.dispatch('sendEventToBus', {
|
|
86
|
+
code: 'DISPLAY-MAIL-CONSENT',
|
|
87
|
+
payload: {},
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
isMailConsentUpdating,
|
|
93
|
+
isMailConsentUpdated,
|
|
94
|
+
errorUpdateMailConsent,
|
|
95
|
+
handleUpdateConsent,
|
|
96
|
+
handleRetry,
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
</script>
|
|
101
|
+
|
|
102
|
+
<style lang="scss" scoped>
|
|
103
|
+
@import 'pb-variables';
|
|
104
|
+
|
|
105
|
+
.pb-consent {
|
|
106
|
+
&__label {
|
|
107
|
+
@include set-font-face('regular');
|
|
108
|
+
@include set-font-scale('06', 'm');
|
|
109
|
+
|
|
110
|
+
color: $color-grey-900;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
&__actions {
|
|
114
|
+
width: 100%;
|
|
115
|
+
|
|
116
|
+
&--notification {
|
|
117
|
+
width: 100%;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
&--button {
|
|
121
|
+
margin-top: $mu150;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
&__loading {
|
|
126
|
+
margin: auto;
|
|
127
|
+
padding-top: $mu100;
|
|
128
|
+
|
|
129
|
+
&-loader {
|
|
130
|
+
height: $mu300;
|
|
131
|
+
width: $mu300;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
</style>
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Story, Meta, Canvas } from '@storybook/addon-docs';
|
|
2
|
+
import { nestedAppDecorator } from '../../../../.storybook/nested-app-decorator';
|
|
3
|
+
import PbProjectHub from './PbProjectHub';
|
|
4
|
+
import store from '../../../stores/store';
|
|
5
|
+
import { rest } from 'msw';
|
|
6
|
+
import { internalServerErrorResolver } from '../../../services/api/mocks/commonMock';
|
|
7
|
+
import { getProjectByIdResolver } from '../../../services/api/mocks/projectsMock';
|
|
8
|
+
import {
|
|
9
|
+
getMailConsentToUpdateResolver,
|
|
10
|
+
getMailConsentUpdatedResolver,
|
|
11
|
+
putConsentsUpdateResolver,
|
|
12
|
+
putConsentsUpdateWithDelayResolver,
|
|
13
|
+
} from '../../../services/api/mocks/consentMock';
|
|
14
|
+
|
|
15
|
+
<Meta
|
|
16
|
+
title="Project Booster/Components/Projects/PbProjectHub 🦠/Features/Consent (Mail)"
|
|
17
|
+
component={PbProjectHub}
|
|
18
|
+
parameters={{
|
|
19
|
+
layout: 'fullscreen',
|
|
20
|
+
}}
|
|
21
|
+
decorators={[
|
|
22
|
+
nestedAppDecorator(store, [
|
|
23
|
+
{
|
|
24
|
+
name: 'ProjectHub',
|
|
25
|
+
path: '/',
|
|
26
|
+
component: PbProjectHub,
|
|
27
|
+
beforeEnter: async (to, from, next, store) => {
|
|
28
|
+
store.dispatch('projects/loadProject', '001');
|
|
29
|
+
store.dispatch('consent/checkMailConsent');
|
|
30
|
+
next();
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
]),
|
|
34
|
+
]}
|
|
35
|
+
/>
|
|
36
|
+
|
|
37
|
+
export const TemplateSandbox = (args, { argTypes }) => ({
|
|
38
|
+
props: Object.keys(argTypes),
|
|
39
|
+
template: `<div style="margin: 0 auto;min-height: 100vh;width: 100%;">
|
|
40
|
+
<router-view v-slot="{ Component }">
|
|
41
|
+
<component :is="Component" :key="$route.fullPath" />
|
|
42
|
+
</router-view>
|
|
43
|
+
</div>`,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
# 🦠 `PbProjectHub` - Component
|
|
47
|
+
|
|
48
|
+
The `PbProjectHub` component to access your project.
|
|
49
|
+
|
|
50
|
+
## Showcase with consent section to display
|
|
51
|
+
|
|
52
|
+
<Canvas>
|
|
53
|
+
<Story
|
|
54
|
+
name="Showcase with consent section"
|
|
55
|
+
parameters={{
|
|
56
|
+
controls: { disable: true },
|
|
57
|
+
msw: [
|
|
58
|
+
rest.get('/api/inhabitant-projects/:projectId', getProjectByIdResolver),
|
|
59
|
+
rest.get('/api/users/current/consents', getMailConsentToUpdateResolver),
|
|
60
|
+
rest.put('/api/users/current/consents', putConsentsUpdateResolver),
|
|
61
|
+
],
|
|
62
|
+
}}
|
|
63
|
+
height="100vh"
|
|
64
|
+
>
|
|
65
|
+
{TemplateSandbox.bind({})}
|
|
66
|
+
</Story>
|
|
67
|
+
</Canvas>
|
|
68
|
+
|
|
69
|
+
## Showcase with consent section to hide
|
|
70
|
+
|
|
71
|
+
<Canvas>
|
|
72
|
+
<Story
|
|
73
|
+
name="Showcase with consent section hidden"
|
|
74
|
+
parameters={{
|
|
75
|
+
controls: { disable: true },
|
|
76
|
+
msw: [
|
|
77
|
+
rest.get('/api/inhabitant-projects/:projectId', getProjectByIdResolver),
|
|
78
|
+
rest.get('/api/users/current/consents', getMailConsentUpdatedResolver),
|
|
79
|
+
],
|
|
80
|
+
}}
|
|
81
|
+
height="100vh"
|
|
82
|
+
>
|
|
83
|
+
{TemplateSandbox.bind({})}
|
|
84
|
+
</Story>
|
|
85
|
+
</Canvas>
|
|
86
|
+
|
|
87
|
+
## Showcase with failure on update consent
|
|
88
|
+
|
|
89
|
+
<Canvas>
|
|
90
|
+
<Story
|
|
91
|
+
name="Showcase with update failure"
|
|
92
|
+
parameters={{
|
|
93
|
+
controls: { disable: true },
|
|
94
|
+
msw: [
|
|
95
|
+
rest.get('/api/inhabitant-projects/:projectId', getProjectByIdResolver),
|
|
96
|
+
rest.get('/api/users/current/consents', getMailConsentToUpdateResolver),
|
|
97
|
+
rest.put('/api/users/current/consents', internalServerErrorResolver),
|
|
98
|
+
],
|
|
99
|
+
}}
|
|
100
|
+
height="100vh"
|
|
101
|
+
>
|
|
102
|
+
{TemplateSandbox.bind({})}
|
|
103
|
+
</Story>
|
|
104
|
+
</Canvas>
|
|
105
|
+
|
|
106
|
+
## Showcase with delay on update
|
|
107
|
+
|
|
108
|
+
<Canvas>
|
|
109
|
+
<Story
|
|
110
|
+
name="Showcase with delay on update"
|
|
111
|
+
parameters={{
|
|
112
|
+
controls: { disable: true },
|
|
113
|
+
msw: [
|
|
114
|
+
rest.get('/api/inhabitant-projects/:projectId', getProjectByIdResolver),
|
|
115
|
+
rest.get('/api/users/current/consents', getMailConsentToUpdateResolver),
|
|
116
|
+
rest.put('/api/users/current/consents', putConsentsUpdateWithDelayResolver),
|
|
117
|
+
],
|
|
118
|
+
}}
|
|
119
|
+
height="100vh"
|
|
120
|
+
>
|
|
121
|
+
{TemplateSandbox.bind({})}
|
|
122
|
+
</Story>
|
|
123
|
+
</Canvas>
|
|
@@ -492,6 +492,14 @@
|
|
|
492
492
|
</m-flex>
|
|
493
493
|
</div>
|
|
494
494
|
</m-flex>
|
|
495
|
+
<m-flex v-if="isMailConsentDisplayed" class="pb-project-hub__section" direction="column" full-width>
|
|
496
|
+
<div class="pb-project-hub__section-header">
|
|
497
|
+
<div class="pb-project-hub__section-title" ref="pbConsent">Souscription newsletter</div>
|
|
498
|
+
</div>
|
|
499
|
+
<div class="pb-project-hub__section-content">
|
|
500
|
+
<pb-consent />
|
|
501
|
+
</div>
|
|
502
|
+
</m-flex>
|
|
495
503
|
<m-flex
|
|
496
504
|
v-if="!readOnly"
|
|
497
505
|
class="pb-project-hub__section pb-project-hub__section-separator"
|
|
@@ -794,6 +802,7 @@ import PbMOpinion from '../../mopinion/PbMOpinion';
|
|
|
794
802
|
import PbProjectAttributes from '../project-attributes/PbProjectAttributes';
|
|
795
803
|
import PbRestitution from '../../restitution/PbRestitution';
|
|
796
804
|
import PbTasksPreview from '../../tasks/preview/PbTasksPreview';
|
|
805
|
+
import PbConsent from '../../consent/PbConsent';
|
|
797
806
|
import TASK_MODEL from './plannerTask.json';
|
|
798
807
|
import ESTIMATES_PAYLOAD from './../../estimates/estimates-payload.json';
|
|
799
808
|
import cloneDeep from 'lodash.clonedeep';
|
|
@@ -852,6 +861,7 @@ export default {
|
|
|
852
861
|
PbProjectAttributes,
|
|
853
862
|
PbRestitution,
|
|
854
863
|
PbTasksPreview,
|
|
864
|
+
PbConsent,
|
|
855
865
|
},
|
|
856
866
|
|
|
857
867
|
props: {
|
|
@@ -1001,6 +1011,10 @@ export default {
|
|
|
1001
1011
|
configurationsLoadError: 'getConfigurationsLoadError',
|
|
1002
1012
|
}),
|
|
1003
1013
|
|
|
1014
|
+
...mapGetters('consent', {
|
|
1015
|
+
isMailConsentDisplayed: 'isMailConsentDisplayed',
|
|
1016
|
+
}),
|
|
1017
|
+
|
|
1004
1018
|
...mapGetters('inhabitants', {
|
|
1005
1019
|
isInhabitantDisplayed: 'isInhabitantDisplayed',
|
|
1006
1020
|
currentInhabitantId: 'getCurrentInhabitantId',
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import he from 'he';
|
|
3
|
+
|
|
4
|
+
export const clientApi = axios.create({
|
|
5
|
+
baseURL: '/project-booster/api',
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
if (global.window.config) {
|
|
9
|
+
clientApi.defaults.baseURL = global.window.config.VUE_APP_DEFAULT_BASE_URL;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const updateConsentApiClient = (config) => {
|
|
13
|
+
if (config.apiKey) {
|
|
14
|
+
clientApi.defaults.headers.common['X-ClientApiKey'] = config.apiKey;
|
|
15
|
+
}
|
|
16
|
+
if (config.baseUrl) {
|
|
17
|
+
clientApi.defaults.baseURL = config.baseUrl;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const getConsents = async () => {
|
|
22
|
+
const response = await clientApi.get('/users/current/consents');
|
|
23
|
+
|
|
24
|
+
return JSON.parse(
|
|
25
|
+
JSON.stringify(response.data).replace(/:"([^"]+)"/g, (match, $1) => {
|
|
26
|
+
return `: "${he.escape($1)}"`;
|
|
27
|
+
}),
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const updateMailConsent = async (mailConsent) => {
|
|
32
|
+
return await clientApi.put('/users/current/consents', { mail: mailConsent });
|
|
33
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const getMailConsentToUpdateResolver = (req, res, ctx) => {
|
|
2
|
+
return res.once(
|
|
3
|
+
ctx.status(200),
|
|
4
|
+
ctx.json({
|
|
5
|
+
mail: false,
|
|
6
|
+
}),
|
|
7
|
+
);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const getMailConsentUpdatedResolver = (req, res, ctx) => {
|
|
11
|
+
return res.once(
|
|
12
|
+
ctx.status(200),
|
|
13
|
+
ctx.json({
|
|
14
|
+
mail: true,
|
|
15
|
+
}),
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const putConsentsUpdateResolver = (req, res, ctx) => {
|
|
20
|
+
return res.once(ctx.status(204));
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const putConsentsUpdateWithDelayResolver = (req, res, ctx) => {
|
|
24
|
+
return res.once(ctx.delay(1000), ctx.status(204));
|
|
25
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { updateMailConsent, getConsents } from '../../services/api/consentApi';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
namespaced: true,
|
|
5
|
+
|
|
6
|
+
state: {
|
|
7
|
+
isMailConsentDisplayed: false,
|
|
8
|
+
isMailConsentUpdated: false,
|
|
9
|
+
isMailConsentUpdating: false,
|
|
10
|
+
errorUpdateMailConsent: null,
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
getters: {
|
|
14
|
+
isMailConsentDisplayed(state) {
|
|
15
|
+
return state.isMailConsentDisplayed;
|
|
16
|
+
},
|
|
17
|
+
isMailConsentUpdated(state) {
|
|
18
|
+
return state.isMailConsentUpdated;
|
|
19
|
+
},
|
|
20
|
+
getErrorUpdateMailConsent(state) {
|
|
21
|
+
return state.errorUpdateMailConsent;
|
|
22
|
+
},
|
|
23
|
+
isMailConsentUpdating(state) {
|
|
24
|
+
return state.isMailConsentUpdating;
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
mutations: {
|
|
29
|
+
setIsMailConsentDisplayed(state, isDisplayed) {
|
|
30
|
+
state.isMailConsentDisplayed = isDisplayed;
|
|
31
|
+
},
|
|
32
|
+
setIsMailConsentUpdated(state, isUpdated) {
|
|
33
|
+
state.isMailConsentUpdated = isUpdated;
|
|
34
|
+
},
|
|
35
|
+
setErrorUpdateMailConsent(state, error) {
|
|
36
|
+
state.errorUpdateMailConsent = error;
|
|
37
|
+
},
|
|
38
|
+
setIsMailConsentUpdating(state, isUpdating) {
|
|
39
|
+
state.isMailConsentUpdating = isUpdating;
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
actions: {
|
|
44
|
+
async checkMailConsent({ commit }) {
|
|
45
|
+
try {
|
|
46
|
+
const consents = await getConsents();
|
|
47
|
+
if (!consents.mail) {
|
|
48
|
+
commit('setIsMailConsentDisplayed', true);
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error(error);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
setMailConsentDisplayed({ commit }, isDisplayed) {
|
|
55
|
+
commit('setIsMailConsentDisplayed', isDisplayed);
|
|
56
|
+
},
|
|
57
|
+
async updateMailConsent({ commit }) {
|
|
58
|
+
commit('setIsMailConsentUpdating', true);
|
|
59
|
+
try {
|
|
60
|
+
await updateMailConsent(true);
|
|
61
|
+
commit('setIsMailConsentUpdated', true);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
commit('setErrorUpdateMailConsent', error);
|
|
64
|
+
}
|
|
65
|
+
commit('setIsMailConsentUpdating', false);
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
package/src/stores/store.js
CHANGED
|
@@ -8,6 +8,7 @@ import plannerStore from './modules/plannerStore';
|
|
|
8
8
|
import projectsStore from './modules/projectsStore';
|
|
9
9
|
import toolsStore from './modules/toolsStore';
|
|
10
10
|
import mediaDocumentsStore from './modules/mediaDocumentsStore';
|
|
11
|
+
import consentStore from './modules/consentStore';
|
|
11
12
|
import cloneDeep from 'lodash.clonedeep';
|
|
12
13
|
|
|
13
14
|
export default {
|
|
@@ -25,6 +26,7 @@ export default {
|
|
|
25
26
|
media: cloneDeep(mediaDocumentsStore),
|
|
26
27
|
documentsPlans: cloneDeep(mediaDocumentsStore),
|
|
27
28
|
documentsPictures: cloneDeep(mediaDocumentsStore),
|
|
29
|
+
consent: consentStore,
|
|
28
30
|
},
|
|
29
31
|
mutations: {
|
|
30
32
|
eventBusSendEvent(state, { code, payload }) {
|
package/dist/demo.html
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<!doctype html><meta charset="utf-8"><title>project-booster-vue demo</title><script src="./project-booster-vue.umd.js"></script><link rel="stylesheet" href="./project-booster-vue.css"><script>console.log(project-booster-vue)</script>
|