Skip to content

Commit

Permalink
feat(prompts): video prompt with dedicated settings (V4-1375)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukashroch committed Jan 14, 2025
1 parent 6b345a1 commit 0fbbfab
Show file tree
Hide file tree
Showing 19 changed files with 341 additions and 29 deletions.
4 changes: 3 additions & 1 deletion apps/admin/src/components/prompts/custom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,20 @@ import SelectPrompt from './select-prompt.vue';
import SliderPrompt from './slider-prompt.vue';
import TextareaPrompt from './textarea-prompt.vue';
import TimePickerPrompt from './time-picker-prompt.vue';
import VideoPrompt from './video-prompt.vue';
import YesNoPrompt from './yes-no-prompt.vue';

export default {
AggregateChoicePrompt,
CheckboxListPrompt,
DatePickerPrompt,
InfoPrompt,
NoMoreInformationPrompt,
RadioListPrompt,
SelectPrompt,
SliderPrompt,
TimePickerPrompt,
TextareaPrompt,
VideoPrompt,
YesNoPrompt,
NoMoreInformationPrompt,
};
84 changes: 84 additions & 0 deletions apps/admin/src/components/prompts/custom/video-prompt.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<template>
<v-tabs-window-item key="options" value="options">
<v-row class="mb-3">
<v-col cols="12" md="6">
<v-text-field
class="mb-4"
hide-details="auto"
:label="$t('survey-schemes.prompts.video-prompt.videoId')"
:model-value="videoId"
variant="outlined"
@update:model-value="update('videoId', $event)"
/>
<v-text-field
class="mb-4"
hide-details="auto"
:label="$t('survey-schemes.prompts.video-prompt.height')"
:model-value="height"
variant="outlined"
@update:model-value="updateInteger('height', $event)"
/>
<v-text-field
hide-details="auto"
:label="$t('survey-schemes.prompts.video-prompt.width')"
:model-value="width"
variant="outlined"
@update:model-value="updateInteger('width', $event)"
/>
</v-col>
<v-col cols="12" md="6">
<v-switch
hide-details="auto"
:label="$t('survey-schemes.prompts.video-prompt.autoplay')"
:model-value="autoplay"
@update:model-value="update('autoplay', $event)"
/>
<v-switch
hide-details="auto"
:label="$t('survey-schemes.prompts.video-prompt.required')"
:model-value="required"
@update:model-value="update('required', $event)"
/>
</v-col>
</v-row>
</v-tabs-window-item>
</template>

<script lang="ts">
import { defineComponent, type PropType } from 'vue';
import type { Prompts } from '@intake24/common/prompts';
import { basePrompt } from '../partials';
export default defineComponent({
name: 'VideoPrompt',
mixins: [basePrompt],
props: {
videoId: {
type: String as PropType<Prompts['video-prompt']['videoId']>,
required: true,
},
autoplay: {
type: Boolean as PropType<Prompts['video-prompt']['autoplay']>,
required: true,
},
required: {
type: Boolean as PropType<Prompts['video-prompt']['required']>,
required: true,
},
height: {
type: Number as PropType<Prompts['video-prompt']['height']>,
required: true,
},
width: {
type: Number as PropType<Prompts['video-prompt']['width']>,
required: true,
},
},
});
</script>

<style lang="scss" scoped></style>
10 changes: 7 additions & 3 deletions apps/admin/src/components/prompts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ export type PromptSettings = Record<ComponentType, PromptSettingsRecord>;

export const promptSettings: PromptSettings = {
// Custom
'aggregate-choice-prompt': {
tabs: [...tabs],
sections: ['postMeals'],
},
'checkbox-list-prompt': {
tabs: [...tabsWithValidation],
sections: [...promptSectionsExceptSubmission],
Expand Down Expand Up @@ -79,13 +83,13 @@ export const promptSettings: PromptSettings = {
tabs: [...tabsWithValidation],
sections: [...promptSectionsExceptSubmission],
},
'yes-no-prompt': {
'video-prompt': {
tabs: [...tabs],
sections: [...promptSectionsExceptSubmission],
},
'aggregate-choice-prompt': {
'yes-no-prompt': {
tabs: [...tabs],
sections: ['postMeals'],
sections: [...promptSectionsExceptSubmission],
},
// Standard
'addon-foods-prompt': {
Expand Down
10 changes: 10 additions & 0 deletions apps/docs/admin/surveys/prompt-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,16 @@ Prompt to collect time information.

- `format` - time format (`am/pm` or `24h`)

### Video prompt

Prompt to present Youtube video with optional settings.

- `videoId` - Youtube video ID
- `height` - video height (default: `1280`)
- `width` - video width (default: `720`)
- `autoplay` - autoplay video
- `required` - video is required to be played in full to continue

### Yes/no prompt

Prompt to collect `yes` / `no` (`true` / `false`) information presented as distinct buttons.
1 change: 1 addition & 0 deletions apps/survey/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@coddicat/vue-pinch-scroll-zoom": "^4.7.1",
"@flatten-js/core": "^1.6.2",
"@fortawesome/fontawesome-free": "^6.7.2",
"@vue-youtube/core": "^0.0.6",
"@vueuse/core": "^12.2.0",
"awesome-phonenumber": "^7.2.0",
"axios": "^1.7.9",
Expand Down
2 changes: 2 additions & 0 deletions apps/survey/src/components/prompts/custom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import SelectPrompt from './select-prompt.vue';
import SliderPrompt from './slider-prompt.vue';
import TextareaPrompt from './textarea-prompt.vue';
import TimePickerPrompt from './time-picker-prompt.vue';
import VideoPrompt from './video-prompt.vue';
import YesNoPrompt from './yes-no-prompt.vue';

export * from './aggregate-choice';
Expand All @@ -21,5 +22,6 @@ export default {
RadioListPrompt,
TextareaPrompt,
TimePickerPrompt,
VideoPrompt,
YesNoPrompt,
};
109 changes: 109 additions & 0 deletions apps/survey/src/components/prompts/custom/video-prompt.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<template>
<component
:is="customPromptLayout"
v-bind="{ food, meal, prompt, section, isValid }"
@action="updateAndAction"
>
<template #actions>
<next :disabled="!isValid" @click="updateAndAction('next')" />
</template>
<template #nav-actions>
<next-mobile :disabled="!isValid" @click="updateAndAction('next')" />
</template>
<div class="pa-4">
<div class="iframe-container">
<div ref="youtube" />
</div>
</div>
</component>
</template>

<script lang="ts">
import { usePlayer } from '@vue-youtube/core';
import { computed, defineComponent, ref } from 'vue';
import { usePromptUtils } from '@intake24/survey/composables';
import createBasePrompt from '../createBasePrompt';
const playerStates = {
'YT.PlayerState.UNSTARTED': -1,
'YT.PlayerState.ENDED': 0,
'YT.PlayerState.PLAYING': 1,
'YT.PlayerState.PAUSED': 2,
'YT.PlayerState.BUFFERING': 3,
'YT.PlayerState.CUED': 5,
} as const;
export default defineComponent({
name: 'VideoPrompt',
mixins: [createBasePrompt<'video-prompt'>()],
props: {
modelValue: {
type: String,
default: 'next',
},
},
emits: ['action', 'update:modelValue'],
setup(props, ctx) {
const { action, customPromptLayout } = usePromptUtils(props, ctx);
const youtube = ref();
const watched = ref(false);
const { onStateChange } = usePlayer(props.prompt.videoId, youtube, {
// cookie: false,
playerVars: {
autoplay: props.prompt.autoplay ? 1 : 0,
controls: props.prompt.required ? 0 : 1,
disablekb: props.prompt.required ? 1 : 0,
origin: window.location.origin,
rel: 0,
},
height: props.prompt.height,
width: props.prompt.width,
});
onStateChange((event) => {
const { data } = event;
switch (data) {
case playerStates['YT.PlayerState.ENDED']:
watched.value = true;
break;
default:
break;
}
});
const isValid = computed(() => !props.prompt.required || watched.value);
const state = computed({
get() {
return props.modelValue;
},
set(value) {
ctx.emit('update:modelValue', value);
},
});
const updateAndAction = (type: string, ...args: [id?: string, params?: object]) => {
state.value = type;
action(type, ...args);
};
return {
customPromptLayout,
isValid,
state,
updateAndAction,
youtube,
};
},
});
</script>

<style lang="scss" scoped></style>
2 changes: 2 additions & 0 deletions apps/survey/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import vuetify from './plugins/vuetify';
import router from './router';
import { errorHandler, httpService } from './services';
import { cookieConsentConfig, cookieConsentPlugin } from '@intake24/ui';
import { createManager } from '@vue-youtube/core';

import { useAuth } from './stores';

Expand All @@ -29,6 +30,7 @@ app.use(i18n);
app.use(vuetify);
app.use(VueGtag, { bootstrap: false }, router);
app.use(cookieConsentPlugin, cookieConsentConfig());
app.use(createManager());

app.mount('#app');

Expand Down
26 changes: 12 additions & 14 deletions apps/survey/src/scss/common.scss
Original file line number Diff line number Diff line change
Expand Up @@ -85,20 +85,18 @@
}
}

@media #{map.get(settings.$display-breakpoints, 'sm-and-down')} {
.iframe-container {
position: relative;
padding-bottom: 56.25%;
width: 100%;
height: 0;
overflow: hidden;
.iframe-container {
position: relative;
padding-bottom: 56.25%;
width: 100%;
height: 0;
overflow: hidden;

iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
14 changes: 14 additions & 0 deletions packages/common/src/prompts/custom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,19 @@ export const timePickerPrompt: Prompts['time-picker-prompt'] = copy({
format: '24hr',
});

export const videoPrompt: Prompts['video-prompt'] = copy({
...basePrompt,
component: 'video-prompt',
type: 'custom',
id: 'video-prompt',
name: 'Video prompt',
videoId: '',
height: 720,
width: 1280,
autoplay: false,
required: false,
});

export const yesNoPrompt: Prompts['yes-no-prompt'] = copy({
...basePrompt,
component: 'yes-no-prompt',
Expand All @@ -130,5 +143,6 @@ export const customPrompts = [
radioListPrompt,
textareaPrompt,
timePickerPrompt,
videoPrompt,
yesNoPrompt,
];
Loading

0 comments on commit 0fbbfab

Please sign in to comment.