You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

333 lines
10 KiB

<!--
OnboardingDialog.vue - Welcome & Help Dialog Component
Provides multi-page onboarding experience for new users with step-by-step
guidance through TimeSafari features including feed, discovery, and project creation.
@author Matthew Raymer
@since 2024-07-08
Features:
- Three-page onboarding flow (Home, Discover, Create)
- Dynamic content based on user registration status
- Contact-aware messaging when contacts exist
- Responsive design with mobile-first approach
- Automatic completion tracking in user settings
Migration Status: Enhanced Triple Migration Pattern Complete
- Uses PlatformServiceMixin for database operations
- Service methods for settings and contact operations
- No raw SQL queries (abstracted to service layer)
- Template streamlining with computed properties
-->
<template>
<div v-if="visible" class="dialog-overlay">
<div v-if="page === OnboardPage.Home" class="dialog">
<h1 class="text-xl font-bold text-center mb-4 relative">
Welcome to Time Safari
<br />
- Showcase Impact & Magnify Time
<div :class="closeButtonClasses" @click="onClickClose(true)">
<font-awesome icon="xmark" class="w-[1em]" />
</div>
</h1>
The feed underneath this pop-up shows the latest contributions, some from
people and some from projects.
<p v-if="isRegistered" class="mt-4">
You can now log things that you've seen:
<span v-if="numContacts > 0">
click on any name (like {{ firstContactName }}) or
</span>
click on the
<span class="bg-green-600 text-white rounded-full">
<font-awesome icon="plus" class="fa-fw" />
</span>
button to express your appreciation for... whatever.
</p>
<p class="mt-4">
Once someone registers you, you can log your appreciation, too.
</p>
<p class="mt-4">
The more you illuminate cool things people are doing, the more you
attract people to work with you.
</p>
<p class="mt-4 flex items-center">
The
<font-awesome icon="house-chimney" :class="navigationIconClasses" />
button below brings you back to this feed screen.
</p>
<div class="mt-8">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
<button
type="button"
data-testId="closeOnboardingAndFinish"
:class="secondaryButtonClasses"
@click="onClickClose(true)"
>
That's enough help, thanks.
</button>
<button
type="button"
:class="primaryButtonClasses"
@click="$router.push({ name: 'discover' })"
>
Show me more!
</button>
</div>
</div>
<p class="mt-4 flex items-center">
To see these instructions and more, click above on
<span :class="helpBadgeClasses"> Help </span>
</p>
</div>
<div v-if="page === OnboardPage.Discover" class="dialog">
<h1 class="text-xl font-bold text-center mb-4 relative">
Offer to Interesting Events & People
<div :class="closeButtonClasses" @click="onClickClose(true)">
<font-awesome icon="xmark" class="w-[1em]" />
</div>
</h1>
<p>
Once you've seen things that others have given or done, you may find
ways you want to contribute, too. It turns out others have proposed
activities together, and this page is where you find projects.
</p>
<p class="mt-4">
Search for a topic, or search around your neighborhood under "Nearby".
</p>
<p class="mt-4">
When you find some that seem interesting, you can offer your help. You
are welcome to make your offer conditional, for example if they get 2
other people to help besides you.
</p>
<p class="mt-4 flex items-center">
The
<font-awesome icon="magnifying-glass" :class="navigationIconClasses" />
button below brings you to this discovery screen.
</p>
<div class="mt-8">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
<button
type="button"
data-testId="closeOnboardingAndFinish"
:class="secondaryButtonClasses"
@click="onClickClose(true)"
>
No more help, thanks.
</button>
<button
type="button"
:class="primaryButtonClasses"
@click="$router.push({ name: 'projects' })"
>
Show me even more.
</button>
</div>
</div>
</div>
<div v-if="page === OnboardPage.Create" class="dialog">
<h1 class="text-xl font-bold text-center mb-4 relative">
Fish for Others with Your Projects
<div :class="closeButtonClasses" @click="onClickClose(true)">
<font-awesome icon="xmark" class="w-[1em]" />
</div>
</h1>
<p class="relative">
Now you can take a turn: click on the
<span class="bg-green-600 text-white rounded-full">
<font-awesome icon="plus" class="fa-fw" />
</span>
button to throw out projects of your own... anything you'd like to see
happen. If your first idea doesn't catch anyone, try, try again... and
let others know that this is a good place to find help.
</p>
<p class="mt-4 flex items-center">
The
<font-awesome icon="hand" :class="navigationIconClasses" />
button below brings you here to see your ideas.
</p>
<p class="mt-4">
By the way, one good way to get to know your neighbors and their
interests is to offer time directly to them. You can do this on the
contacts screen
<font-awesome icon="users" class="text-slate-500" />
which is a great way to get to know a neighbor's interests.
</p>
<div class="mt-8">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
<button
type="button"
data-testId="closeOnboardingAndFinish"
:class="secondaryButtonClasses"
@click="onClickClose(true, true)"
>
Let's go!
<br />
See & record gratitude.
</button>
<button
type="button"
:class="primaryButtonClasses"
@click="$router.push({ name: 'help' })"
>
I want to read more Help.
</button>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";
import { Router } from "vue-router";
import { NotificationIface } from "../constants/app";
import { OnboardPage } from "../libs/util";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
@Component({
mixins: [PlatformServiceMixin],
components: { OnboardPage },
})
export default class OnboardingDialog extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void;
$router!: Router;
activeDid = "";
firstContactName = "";
givenName = "";
isRegistered = false;
numContacts = 0;
page = OnboardPage.Home;
visible = false;
/**
* Returns OnboardPage enum for template access
*/
get OnboardPage() {
return OnboardPage;
}
/**
* CSS classes for primary action buttons (blue gradient)
*/
get primaryButtonClasses() {
return "block w-full text-center text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2";
}
/**
* CSS classes for secondary action buttons (slate gradient)
*/
get secondaryButtonClasses() {
return "block w-full text-center text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2";
}
/**
* CSS classes for close button in dialog headers
*/
get closeButtonClasses() {
return "text-lg text-center leading-none absolute right-0 -top-1";
}
/**
* CSS classes for navigation icons in explanatory text
*/
get navigationIconClasses() {
return "ml-1 mr-1 text-lg text-white bg-slate-400 px-2 py-2 rounded";
}
/**
* CSS classes for help badge styling
*/
get helpBadgeClasses() {
return "ml-1 mr-1 text-xs uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-1 rounded-md";
}
/**
* Opens the onboarding dialog on the specified page
* Loads user settings and contact data to customize the experience
*
* @param page - The onboarding page to display
*/
async open(page: OnboardPage) {
this.page = page;
const settings = await this.$accountSettings();
this.activeDid = settings.activeDid || "";
this.isRegistered = !!settings.isRegistered;
const contacts = await this.$getAllContacts();
this.numContacts = contacts.length;
if (contacts.length > 0) {
this.firstContactName = contacts[0].name || "";
}
this.visible = true;
if (this.page === OnboardPage.Create) {
// we'll assume that they've been through all the other pages
await this.$updateSettings({
finishedOnboarding: true,
});
}
}
/**
* Closes the onboarding dialog with optional completion actions
*
* @param done - Whether to mark onboarding as complete
* @param goHome - Whether to navigate to home after closing
*/
async onClickClose(done?: boolean, goHome?: boolean) {
this.visible = false;
if (done) {
await this.$updateSettings({
finishedOnboarding: true,
});
if (goHome) {
this.$router.push({ name: "home" });
}
}
}
}
</script>
<style>
.dialog-overlay {
z-index: 40;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
padding: 1.5rem;
}
.dialog {
background-color: white;
padding: 1rem;
border-radius: 0.5rem;
width: 100%;
max-width: 500px;
}
</style>