feat(onboarding): add OnboardingWidget component

This commit is contained in:
Jennie Robinson Faber 2026-04-09 22:44:41 +01:00
parent 3797ff7925
commit 3ff7cd4e0b

View file

@ -0,0 +1,215 @@
<template>
<ClientOnly>
<div v-if="!loading" class="onboarding-widget dashed-box no-hover">
<!-- Welcome mode: onboarding in progress -->
<template v-if="!isComplete">
<div class="ow-header">
<h3 class="ow-title">Welcome to Ghost Guild</h3>
<p class="ow-intro">Get oriented here are a few things to explore as a new member.</p>
</div>
<div class="ow-suggestion">
<span class="ow-suggestion-text">{{ currentSuggestion.text }}</span>
<NuxtLink
v-if="currentSuggestion.action && !currentSuggestion.isExternal"
:to="currentSuggestion.action"
class="btn btn-primary ow-action"
>
{{ currentSuggestion.actionText }}
</NuxtLink>
<a
v-else-if="currentSuggestion.isExternal"
href="https://wiki.ghostguild.org"
target="_blank"
rel="noopener"
class="btn btn-primary ow-action"
@click="trackGoal('wikiClicked')"
>
{{ currentSuggestion.actionText }}
</a>
</div>
<div class="ow-progress">
<span class="ow-progress-label">{{ completedCount }} of 4</span>
<span class="ow-dots">
<span
v-for="i in 4"
:key="i"
class="ow-dot"
:class="{ 'ow-dot--done': i <= completedCount }"
/>
</span>
</div>
</template>
<!-- Suggestion mode: onboarding complete -->
<template v-else>
<!-- Empty state -->
<div v-if="currentSuggestion.key === 'empty'" class="ow-empty">
{{ currentSuggestion.text }}
</div>
<!-- Event recommendation -->
<div v-else-if="currentSuggestion.key === 'event'" class="ow-rec">
<div class="section-label">Suggested</div>
<span class="ow-rec-text">{{ currentSuggestion.text }}</span>
<NuxtLink
:to="currentSuggestion.action"
class="ow-rec-link"
>
{{ currentSuggestion.actionText }} &rarr;
</NuxtLink>
</div>
<!-- Ecology recommendation -->
<div v-else-if="currentSuggestion.key === 'ecology'" class="ow-rec">
<div class="section-label">Suggested</div>
<span class="ow-rec-text">{{ currentSuggestion.text }}</span>
<NuxtLink
:to="currentSuggestion.action"
class="ow-rec-link"
>
{{ currentSuggestion.actionText }} &rarr;
</NuxtLink>
</div>
<!-- Wiki recommendation -->
<div v-else-if="currentSuggestion.key === 'wiki'" class="ow-rec">
<div class="section-label">Suggested</div>
<span class="ow-rec-text">{{ currentSuggestion.text }}</span>
<a
v-if="currentSuggestion.action"
:href="currentSuggestion.action"
target="_blank"
rel="noopener"
class="ow-rec-link"
@click="trackGoal('wikiClicked')"
>
{{ currentSuggestion.actionText }} &rarr;
</a>
</div>
</template>
</div>
</ClientOnly>
</template>
<script setup>
const { goals, isComplete, currentSuggestion, trackGoal, loading } = useOnboarding()
const completedCount = computed(() => {
const g = goals.value
return [g.hasProfileTags, g.hasVisitedEvent, g.hasEngagedEcology, g.hasClickedWiki]
.filter(Boolean).length
})
</script>
<style scoped>
.onboarding-widget {
padding: 16px 20px;
}
/* Welcome mode */
.ow-header {
margin-bottom: 12px;
}
.ow-title {
font-family: var(--font-display);
font-size: 16px;
font-weight: 600;
color: var(--text-bright);
margin: 0 0 4px;
}
.ow-intro {
font-size: 12px;
color: var(--text-dim);
margin: 0;
line-height: 1.5;
}
.ow-suggestion {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 0;
border-top: 1px dashed var(--border);
flex-wrap: wrap;
}
.ow-suggestion-text {
font-size: 12px;
color: var(--text);
line-height: 1.5;
}
.ow-action {
flex-shrink: 0;
font-size: 11px;
padding: 5px 14px;
}
.ow-progress {
display: flex;
align-items: center;
gap: 8px;
padding-top: 10px;
border-top: 1px dashed var(--border);
}
.ow-progress-label {
font-size: 10px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--text-faint);
}
.ow-dots {
display: flex;
gap: 4px;
}
.ow-dot {
width: 6px;
height: 6px;
border: 1px dashed var(--border);
display: inline-block;
}
.ow-dot--done {
background: var(--candle);
border-color: var(--candle);
border-style: solid;
}
/* Suggestion mode (graduated) */
.ow-rec {
display: flex;
flex-direction: column;
gap: 4px;
}
.ow-rec .section-label {
margin-bottom: 2px;
}
.ow-rec-text {
font-size: 12px;
color: var(--text);
line-height: 1.5;
}
.ow-rec-link {
font-size: 11px;
color: var(--candle);
margin-top: 2px;
display: inline-block;
}
.ow-empty {
font-size: 12px;
color: var(--text-faint);
line-height: 1.5;
}
</style>