Add WIP onboarding checklist behind the flag (#19801)

ref https://linear.app/tryghost/issue/IPC-66/onboarding-checklist-v1

- Adds a basic version of a new onboarding checklist behind the feature
flag, without incomplete/complete state logic
- Links to Design settings, Members screen and new post
- Opens amodal that we’ll use as Share modal

---------

Co-authored-by: Daniël van der Winden <danielvanderwinden@ghost.org>
This commit is contained in:
Djordje Vlaisavljevic 2024-03-18 14:53:01 +00:00 committed by GitHub
parent 48ae822b9f
commit a67342b06a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 548 additions and 21 deletions

View File

@ -55,6 +55,10 @@ const features = [{
title: 'New email addresses',
description: 'For self hosters, forces the usage of the mail.from config as from address for all outgoing emails',
flag: 'newEmailAddresses'
},{
title: 'Onboarding checklist',
description: 'Onboarding checklist that helps new customers get started',
flag: 'onboardingChecklist'
},{
title: 'NestJS Playground',
description: 'Wires up the Ghost NestJS App to the Admin API',

View File

@ -0,0 +1,86 @@
<section class="gh-dashboard-section">
<article class="gh-dashboard-onboarding-box">
<div class="gh-dashboard-onboarding-box-message">
<div>
{{!-- {{svg-jar "confetti"}} --}}
<h3 class="gh-dashboard-onboarding-heading">Welcome to your new Ghost publication!</h3>
<p>Lets set you up for success.</p>
<p>For additional resources, visit our Help Center.</p>
</div>
<span>Dismiss</span>
</div>
<div class="gh-dashboard-onboarding-items">
<div>
{{!-- Step 1 --}}
<div class="gh-dashboard-onboarding-item gh-dashboard-onboarding-item--completed">
<div class="gh-dashboard-onboarding-item-content">
{{svg-jar "rocket" }}
<span class="gh-dashboard-list-text">Start a new Ghost publication</span>
</div>
<div class="gh-dashboard-onboarding-item-checkmark">
{{svg-jar "check-2" }}
</div>
</div>
{{!-- Step 2 --}}
<LinkTo @route="settings-x.settings-x" @model="design/edit?ref=setup" class="gh-dashboard-onboarding-item gh-dashboard-onboarding-item--default">
<div class="gh-dashboard-onboarding-item-content">
{{svg-jar "brush" }}
<div class="gh-dashboard-onboarding-item-details">
<span class="gh-dashboard-onboarding-item-title">Customize your publication</span>
<span class="gh-dashboard-onboarding-item-description">Match the look and feel to your style and make it yours.</span>
</div>
</div>
<div class="gh-dashboard-onboarding-item-action">
{{svg-jar "arrow-right" }}
</div>
</LinkTo>
{{!-- Step 3 --}}
<LinkTo @route="lexical-editor.new" @model="post" class="gh-dashboard-onboarding-item">
<div class="gh-dashboard-onboarding-item-content">
{{svg-jar "writing" }}
<div class="gh-dashboard-onboarding-item-details">
<span class="gh-dashboard-onboarding-item-title">Create your first post</span>
<span class="gh-dashboard-onboarding-item-description">Explore the editor and tell your story — or a preview of whats to come.</span>
</div>
</div>
<div class="gh-dashboard-onboarding-item-action">
{{svg-jar "arrow-right" }}
</div>
</LinkTo>
{{!-- Step 4 --}}
<LinkTo @route="members" class="gh-dashboard-onboarding-item">
<div class="gh-dashboard-onboarding-item-content">
{{svg-jar "member-add" }}
<div class="gh-dashboard-onboarding-item-details">
<span class="gh-dashboard-onboarding-item-title">Build your audience</span>
<span class="gh-dashboard-onboarding-item-description">Add members and grow your readership.</span>
</div>
</div>
<div class="gh-dashboard-onboarding-item-action">
{{svg-jar "arrow-right" }}
</div>
</LinkTo>
{{!-- Step 5 --}}
<div role="button" {{on "click" (toggle-action "showShareModal" this)}} class="gh-dashboard-onboarding-item">
<div class="gh-dashboard-onboarding-item-content">
{{svg-jar "megaphone" }}
<div class="gh-dashboard-onboarding-item-details">
<span class="gh-dashboard-onboarding-item-title">Share your publication</span>
<span class="gh-dashboard-onboarding-item-description">Share your publication on social media.</span>
</div>
</div>
<div class="gh-dashboard-onboarding-item-action">
{{svg-jar "arrow-right" }}
</div>
</div>
</div>
</div>
</article>
</section>
{{#if this.showShareModal}}
<GhFullscreenModal @modal="share"
@close={{this.close}}
@modifier="action wide"
/>
{{/if}}

View File

@ -0,0 +1,34 @@
import Component from '@glimmer/component';
import {action} from '@ember/object';
import {tracked} from '@glimmer/tracking';
export default class OnboardingChecklist extends Component {
@tracked customizePublication = false;
@tracked createPost = false;
@tracked buildAudience = false;
@tracked tellWorld = false;
@tracked showMemberTierModal = false;
@action
completeStep(step) {
this.completed = !this.completed;
switch (step) {
case 'customizePublication':
this.customizePublication = !this.customizePublication;
break;
case 'createPost':
this.createPost = !this.createPost;
break;
case 'buildAudience':
this.buildAudience = !this.buildAudience;
break;
case 'tellWorld':
this.tellWorld = !this.tellWorld;
break;
default:
break;
}
}
}

View File

@ -0,0 +1,116 @@
<header class="modal-header">
<h1>Share your publication</h1>
<p>Let the world discover your publication. Share it with your network and start building a community of readers.</p>
</header>
<button type="button" class="close" title="Close">{{svg-jar "close"}}<span class="hidden">Close</span></button>
{{!-- <div class="modal-body">
<div class="gh-post-bookmark-container">
<div class="gh-post-bookmark">
{{#let (or @post.featureImage (get-setting "coverImage")) as |imageUrl|}}
<div class="gh-post-bookmark-title">{{get-setting "title"}}</div>
<div class="gh-post-bookmark-description truncate">{{ get-setting "description"}}</div>
<div class="gh-post-bookmark-details">
{{#if (get-setting "icon")}}
<div class="gh-post-bookmark-site-icon"><img src={{get-setting "icon"}} alt="" role="presentation" /></div>
{{/if}}
<div class="gh-post-bookmark-site-title">{{get-setting "title"}}</div>
<div class="gh-post-bookmark-authors">Daniël van der Winden</div>
</div>
{{#if imageUrl}}
<div class="gh-post-bookmark-image">
<img src={{imageUrl}} alt="" role="presentation" />
</div>
{{/if}}
{{/let}}
</div>
</div>
<span>You can set your publication icon, cover image and description in Settings</span>
<ul>
<li>
<span>Icon</span>
<p>Copy a link to your publication</p>
</li>
<li>
<span>Icon</span>
<p>Share on X</p>
</li>
<li>
<span>Icon</span>
<p>Share on Facebook</p>
</li>
<li>
<span>Icon</span>
<p>Share on LinkedIn</p>
</li>
</ul>
</div> --}}
<div class="modal-body">
<div class="gh-site-preview-container">
<div class="gh-site-preview">
{{#let (or @post.featureImage (get-setting "coverImage")) as |imageUrl|}}
<div class="gh-site-preview-title">{{get-setting "title"}}</div>
<div class="gh-site-preview-description truncate">{{ get-setting "description"}}</div>
<div class="gh-site-preview-details">
{{#if (get-setting "icon")}}
<div class="gh-site-icon"><img src={{get-setting "icon"}} alt="" role="presentation" /></div>
{{/if}}
<div class="gh-site-title">{{get-setting "title"}}
<span>•</span>
</div>
<div class="gh-authors">Daniël van der Winden</div>
</div>
{{#if imageUrl}}
<div class="gh-site-image">
<img src={{imageUrl}} alt="" role="presentation" />
</div>
{{/if}}
{{/let}}
</div>
</div>
<span class="tip">You can set your publication icon, cover image and description in Settings</span>
<ul class="gh-share-links">
<li>
<span>
{{svg-jar "link"}}
</span>
Copy a link to your publication
<div class="gh-dashboard-onboarding-item-action">
{{svg-jar "arrow-right"}}
</div>
</li>
<li>
<span>
{{svg-jar "social-x"}}
</span>
Share on X
<div class="gh-dashboard-onboarding-item-action">
{{svg-jar "arrow-right"}}
</div>
</li>
<li>
<span>
{{svg-jar "social-facebook"}}
</span>
Share on Facebook
<div class="gh-dashboard-onboarding-item-action">
{{svg-jar "arrow-right"}}
</div>
</li>
<li>
<span>
{{svg-jar "social-linkedin"}}
</span>
Share on LinkedIn
<div class="gh-dashboard-onboarding-item-action">
{{svg-jar "arrow-right"}}
</div>
</li>
</ul>
</div>

View File

@ -0,0 +1,5 @@
import ModalComponent from 'ghost-admin/components/modal-base';
export default ModalComponent.extend({
});

View File

@ -80,6 +80,7 @@ export default class FeatureService extends Service {
@feature('filterEmailDisabled') filterEmailDisabled;
@feature('adminXDemo') adminXDemo;
@feature('portalImprovements') portalImprovements;
@feature('onboardingChecklist') onboardingChecklist;
_user = null;

View File

@ -2759,4 +2759,262 @@ Dashboard Mentions */
padding: 0;
font-size: 1.4rem;
font-weight: 500;
}
/* ---------------------------------
Onboarding checklist */
.gh-dashboard-onboarding-box {
display: grid;
grid-gap: 24px;
grid-template-columns: 1fr 2fr;
border: 1px solid #ebeef0;
border-radius: 6px;
overflow: hidden;
flex: 1;
}
.gh-dashboard-onboarding-box-message {
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: rgb(204, 29, 204);
padding: 24px;
color: white;
background: transparent;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
background-image: url(img/gradient-bg.png);
}
.gh-dashboard-onboarding-heading {
font-size: 1.8rem;
font-weight: 700;
line-height: 1.3;
padding: 0;
color: #fff;
letter-spacing: -.3px;
}
.gh-dashboard-onboarding-items {
padding: 24px;
}
.gh-dashboard-onboarding-item {
padding: 12px 0;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #ebeef0;
}
.gh-dashboard-onboarding-item:first-child {
margin-top: -12px;
}
.gh-dashboard-onboarding-item:last-child {
border-bottom: 0 none;
margin-bottom: -12px;
}
.gh-dashboard-onboarding-item:not(.gh-dashboard-onboarding-item--completed):hover {
background: linear-gradient(315deg,#fafafb 60%,#fff);
}
.gh-dashboard-onboarding-item--completed .gh-dashboard-onboarding-item-content {
opacity: 0.3;
}
.gh-dashboard-onboarding-item-content {
display: flex;
align-items: flex-start;
justify-content: flex-start;
min-width: 0;
}
.gh-dashboard-onboarding-item-details {
display: flex;
flex-direction: column;
text-align: left;
}
.gh-dashboard-onboarding-item-title {
font-weight: 600;
font-size: 1.5rem;
letter-spacing: 0;
color: #394047;
padding: 0 32px 0 0;
text-decoration: none;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
margin-bottom: 0;
}
.gh-dashboard-onboarding-items:hover .gh-dashboard-onboarding-item:hover .gh-dashboard-onboarding-item-description,
.gh-dashboard-onboarding-items .gh-dashboard-onboarding-item--default .gh-dashboard-onboarding-item-description {
height: 20px;
}
.gh-dashboard-onboarding-item-description, .gh-dashboard-onboarding-items:hover .gh-dashboard-onboarding-item-description {
font-weight: 400;
color: #626d79!important;
height: 0;
overflow: hidden;
transition: height .15s;
}
.gh-dashboard-onboarding-item-action {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
min-width: 0;
}
.gh-dashboard-onboarding-item-content svg {
width: 20px;
min-width: 20px;
height: auto;
margin: 2px 16px 0 0;
color: #ae5aef;
}
.gh-dashboard-onboarding-item-content svg path {
stroke: #ae5aef;
stroke-width: 1.5 !important;
}
.gh-dashboard-onboarding-item--completed .gh-dashboard-onboarding-item-content svg {
color: var(--middarkgrey);
}
.gh-dashboard-onboarding-item--completed .gh-dashboard-onboarding-item-content svg path {
stroke: var(--middarkgrey);
}
.gh-dashboard-onboarding-item-action {
display: flex;
}
.gh-dashboard-onboarding-item-action svg {
width: 14px;
min-width: 14px;
height: 14px;
margin-right: 1rem;
}
.gh-dashboard-onboarding-item-action svg path {
fill: var(--midlightgrey);
}
.gh-dashboard-onboarding-item-checkmark {
display: flex;
}
.gh-dashboard-onboarding-item-checkmark svg {
width: 14px;
height: auto;
margin-right: 1rem;
}
.gh-dashboard-onboarding-item-checkmark svg path {
stroke: var(--green);
}
/* ---------------------------------
Share publication modal */
.gh-site-preview-container {
padding: 24px;
border-radius: 6px;
border: 1px solid var(--whitegrey);
background: var(--white);
transition: all .3s ease-in-out;
}
.gh-site-preview {
}
.gh-site-preview-title {
font-size: 1.6rem;
font-weight: 700;
line-height: 1.3;
padding: 0;
color: var(--black);
letter-spacing: -.3px;
margin-bottom: 4px;
width: 100%;
}
.gh-site-preview-description {
font-size: 1.4rem;
font-weight: 400;
line-height: 1.5;
padding: 0;
color: var(--midgrey);
margin-bottom: 24px;
width: 100%;
}
.gh-site-preview-details {
padding: 0;
margin: 0;
width: 100%;
display: flex;
flex-direction: row;
}
.gh-site-icon {
width: 16px;
height: 16px;
margin-bottom: 16px;
}
.gh-site-image {
width: 100%;
height: auto;
border-radius: 6px;
margin-bottom: 24px;
}
.gh-share-links {
list-style: none;
padding: 0;
margin: 40px 0 0;
}
.gh-share-links li {
padding: 8px 0;
border-bottom: 1px solid var(--whitegrey);
display: flex;
flex-direction: row;
font-size: 1.5rem;
font-weight: 600;
color: var(--darkgrey);
line-height: 1.5;
width: 100%;
}
.gh-share-links li span {
display: block;
}
.gh-share-links li span svg {
width: 16px;
height: 16px;
fill: var(--midgrey);
}
span.tip {
font-size: 1.2rem;
font-weight: 400;
line-height: 1.5;
padding: 0;
margin-top: 8px;
color: var(--midgrey);
width: 100%;
display: block;
}

View File

@ -83,31 +83,38 @@
<GhLoadingSpinner />
{{else}}
{{#if this.areMembersEnabled}}
{{#if (feature 'onboardingChecklist')}}
<Dashboard::OnboardingChecklist />
{{/if}}
{{#if this.hasPaidTiers}}
<Dashboard::Charts::Overview />
{{/if}}
<div class="gh-dashboard-group {{if this.isTotalMembersZero 'is-zero'}}">
<Dashboard::Charts::AnchorAttribution />
{{#if this.hasPaidTiers}}
<section class="gh-dashboard-section">
<article class="gh-dashboard-box gh-dashboard-minicharts-attribution">
<Dashboard::Charts::PaidBreakdown />
<Dashboard::Charts::PaidMix />
</article>
</section>
{{/if}}
{{#unless this.membersUtils.isMembersInviteOnly}}
<Dashboard::Charts::Attribution />
{{/unless}}
{{#if this.areNewslettersEnabled}}
<Dashboard::Charts::Engagement />
{{/if}}
{{#unless (feature 'onboardingChecklist')}}
<div class="gh-dashboard-group {{if this.isTotalMembersZero 'is-zero'}}">
<Dashboard::Charts::AnchorAttribution />
{{#if this.hasPaidTiers}}
<section class="gh-dashboard-section">
<article class="gh-dashboard-box gh-dashboard-minicharts-attribution">
<Dashboard::Charts::PaidBreakdown />
<Dashboard::Charts::PaidMix />
</article>
</section>
{{/if}}
{{#unless this.membersUtils.isMembersInviteOnly}}
<Dashboard::Charts::Attribution />
{{/unless}}
{{#if this.areNewslettersEnabled}}
<Dashboard::Charts::Engagement />
{{/if}}
{{#if this.isTotalMembersZero}}
<Dashboard::Parts::Zero />
{{/if}}
</div>
{{#if this.isTotalMembersZero}}
<Dashboard::Parts::Zero />
{{/if}}
</div>
{{/unless}}
{{/if}}
<div class="gh-dashboard-recents-mentions">

View File

@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24" id="Color-Brush-3--Streamline-Ultimate.svg"><desc>Color Brush 3 Streamline Icon: https://streamlinehq.com</desc><path d="M1.382 22.522a0.552 0.552 0 0 1 0.03 -1.01c2.629 -1.065 1.781 -4.062 2.563 -5.664a3.931 3.931 0 0 1 5.22 -1.8c5.869 2.867 -1.117 11.661 -7.813 8.474Z" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m13.889 14.1 8.778 -11.116a1.353 1.353 0 0 0 -2.018 -1.8l-9.892 9.866" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 655 B

View File

@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24" id="Megaphone--Streamline-Ultimate.svg"><desc>Megaphone Streamline Icon: https://streamlinehq.com</desc><path d="M6.75 15.25H4.5a3.75 3.75 0 0 1 0 -7.5h2.25Z" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M6.75 15.25a22.3 22.3 0 0 1 12.366 3.744l1.134 0.756V3.25l-1.134 0.756A22.3 22.3 0 0 1 6.75 7.75Z" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m23.25 10 0 3" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M6.75 15.25A7.239 7.239 0 0 0 9 20.5" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 855 B

View File

@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24" id="Space-Rocket-Flying--Streamline-Ultimate.svg"><desc>Space Rocket Flying Streamline Icon: https://streamlinehq.com</desc><path d="m22.5 1.567 -2.158 0.24a7.5 7.5 0 0 0 -4.475 2.151L6.06 13.765l4.24 4.242L20.109 8.2a7.494 7.494 0 0 0 2.151 -4.475Z" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m6.06 13.765 -3.622 -1.208a0.749 0.749 0 0 1 -0.293 -1.241l0.232 -0.232a6 6 0 0 1 6.14 -1.45l1.255 0.418Z" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m6.06 13.765 -3.622 -1.208a0.749 0.749 0 0 1 -0.293 -1.241l0.232 -0.232a6 6 0 0 1 6.14 -1.45l1.255 0.418Z" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m10.3 18.007 1.208 3.622a0.749 0.749 0 0 0 1.241 0.293l0.233 -0.232a6 6 0 0 0 1.449 -6.14l-0.416 -1.25Z" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m10.3 18.007 1.208 3.622a0.749 0.749 0 0 0 1.241 0.293l0.233 -0.232a6 6 0 0 0 1.449 -6.14l-0.416 -1.25Z" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M7.779 18.538a2.243 2.243 0 0 1 -0.659 1.591c-0.878 0.878 -5.3 2.121 -5.3 2.121s1.243 -4.425 2.121 -5.3a2.246 2.246 0 0 1 1.444 -0.655" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,11 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_537_833)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.77778 16H14.2222C15.2041 16 16 15.2041 16 14.2222V1.77778C16 0.795938 15.2041 0 14.2222 0H1.77778C0.795938 0 0 0.795938 0 1.77778V14.2222C0 15.2041 0.795938 16 1.77778 16Z" fill="#007EBB"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.7782 13.7777H11.4039V9.73375C11.4039 8.625 10.9826 8.0054 10.105 8.0054C9.15035 8.0054 8.65156 8.65019 8.65156 9.73375V13.7777H6.3634V6.07402H8.65156V7.11171C8.65156 7.11171 9.33955 5.83866 10.9743 5.83866C12.6084 5.83866 13.7782 6.83649 13.7782 8.90022V13.7777ZM3.63362 5.06528C2.85422 5.06528 2.22266 4.42876 2.22266 3.64373C2.22266 2.85869 2.85422 2.22217 3.63362 2.22217C4.41302 2.22217 5.04421 2.85869 5.04421 3.64373C5.04421 4.42876 4.41302 5.06528 3.63362 5.06528ZM2.45211 13.7777H4.83808V6.07402H2.45211V13.7777Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_537_833">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24" id="Pencil-Write--Streamline-Ultimate.svg"><desc>Pencil Write Streamline Icon: https://streamlinehq.com</desc><path d="m13.045 14.136 -3.712 0.531 0.53 -3.713 9.546 -9.546a2.25 2.25 0 0 1 3.182 3.182Z" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m18.348 2.469 3.182 3.182" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M18.75 14.25v7.5a1.5 1.5 0 0 1 -1.5 1.5h-15a1.5 1.5 0 0 1 -1.5 -1.5v-15a1.5 1.5 0 0 1 1.5 -1.5h7.5" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 757 B

View File

@ -48,7 +48,8 @@ const ALPHA_FEATURES = [
'importMemberTier',
'lexicalIndicators',
// 'adminXOffers',
'adminXDemo'
'adminXDemo',
'onboardingChecklist'
];
module.exports.GA_KEYS = [...GA_FEATURES];