Skip to content
This repository has been archived by the owner on Dec 22, 2023. It is now read-only.

Improve onboarding #218

Merged
merged 4 commits into from
Oct 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions services/website/cypress/integration/onboarding.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ describe("/onboarding", () => {
});

it("should create a new website", () => {
cy.get('input[name="firstname"]').type("Tester");
cy.get('input[name="url"]').type("local-testing.com");
cy.get('input[id="firstName"]').type("Tester");
cy.get("button[type=button]").click();
cy.get('input[id="url"]').type("local-testing.com");
cy.get('select[name="timezone"]').select("America/Toronto");
cy.get("button[type=submit]").click();
cy.get("button[type=button]").click();
});
});
35 changes: 35 additions & 0 deletions services/website/src/components/onboarding/step-1.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script lang="ts">
import onboarding from "../../stores/onboarding";
import StepWrapper from "./step-wrapper.svelte";

let firstName = "";

const handleSubmit = () => {
$onboarding.user.firstName = firstName;
};

const handleKeyup = (event) => {
if (event.code === "Enter") {
handleSubmit();
}
};
</script>

<StepWrapper>
<div>
<label for="firstName" class="text-sm font-medium leading-5 text-gray-700">
What's your first name?
</label>
<div class="mt-1 rounded-md shadow-sm">
<input id="firstName" bind:value={firstName} on:keyup|preventDefault={handleKeyup} type="text" required class="appearance-none w-full px-3 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5">
</div>
</div>

<div class="mt-6">
<span class="w-full rounded-md shadow-sm">
<button type="button" on:click={handleSubmit} class="w-full flex justify-center mt-4 py-4 px-10 bg-blue-600 rounded-lg font-semibold text-white sm:mt-0">
Next: Configure your website
</button>
</span>
</div>
</StepWrapper>
98 changes: 98 additions & 0 deletions services/website/src/components/onboarding/step-2.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<script lang="ts">
import { onDestroy } from "svelte";
import { addNewWebsite } from "../../api/onboarding";
import { fetchUniqueVisitorsOnOnboardingPage } from "../../api/stats";
import dateRange from "../../stores/date-range";
import onboarding from "../../stores/onboarding";
import { session } from "../../stores/session";
import StepWrapper from "./step-wrapper.svelte";
import TimezoneSelect from "../timezone-select.svelte";

export let hasUserAlreadyConfiguredSite: boolean;
export let isUrlInvalid: boolean;

let fetchUniqueVisitorsInterval: number;

$: existingUserSites = Object.keys($onboarding.user.sites || {}) || [];
$: hasUserAlreadyConfiguredSite = existingUserSites.includes($onboarding.url);
$: isUrlInvalid = hasUserAlreadyConfiguredSite || $onboarding.isSiteAlreadyConfiguredGlobally;

const handleSubmit = async () => {
const firstName = $onboarding.user.firstName;
const url = $onboarding.url;
const timezone = $onboarding.timezone;

const responseCode = await addNewWebsite({firstName, url, timezone});
switch (responseCode) {
case 201:
$onboarding.isSiteAdded = true;
$session.user.sites[url] = {};
dateRange.setPreset("today");
fetchUniqueVisitorsInterval = setInterval(() => {
fetchUniqueVisitorsOnOnboardingPage(window.fetch, window.location.hostname, url);
}, 10000);
break;
case 400:
$onboarding.isSiteAlreadyConfiguredGlobally = true;
break;
}
};

onDestroy(() => {
if (fetchUniqueVisitorsInterval) {
window.clearInterval(fetchUniqueVisitorsInterval);
}
});
</script>

<style>
button:disabled {
@apply bg-indigo-500;
@apply cursor-not-allowed;
}
</style>

<StepWrapper>
<div>
<label for="url" class="text-sm font-medium leading-5 text-gray-700">
What's your website URL?
</label>
<div class="mt-1 relative flex rounded-md shadow-sm">
<span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm">
http(s)://www.
</span>
<input id="url" bind:value={$onboarding.url} on:focus={() => $onboarding.isSiteAlreadyConfiguredGlobally = false} type="text" name="url" required class="px-3 py-2 w-full border border-gray-300 rounded-none rounded-r-md focus:outline-none focus:shadow-outline-blue focus:border-blue-300 sm:text-sm sm:leading-5">
{#if isUrlInvalid && !$onboarding.isSiteAdded}
<div class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
<svg class="h-5 w-5 text-red-500" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
</div>
{/if}
</div>
<p class="mt-2 text-sm text-red-600">
{#if !$onboarding.isSiteAdded && hasUserAlreadyConfiguredSite}
You've already configured this website.
{:else if !$onboarding.isSiteAdded && $onboarding.isSiteAlreadyConfiguredGlobally}
This site has already been configured. If it belongs to you, please contact us.
{/if}
</p>
</div>

<div class="mt-6">
<label for="url" class="text-sm font-medium leading-5 text-gray-700">
What's your preferred reporting timezone
</label>
<div class="mt-1 rounded-md shadow-sm">
<TimezoneSelect name="timezone" />
</div>
</div>

<div class="mt-6">
<span class="w-full rounded-md shadow-sm">
<button type="button" on:click={handleSubmit} disabled={isUrlInvalid} class="w-full flex justify-center mt-4 py-4 px-10 bg-blue-600 rounded-lg font-semibold text-white sm:mt-0">
Let's go
</button>
</span>
</div>
</StepWrapper>
29 changes: 29 additions & 0 deletions services/website/src/components/onboarding/step-3.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<script lang="ts">
import onboarding from "../../stores/onboarding";
import StepWrapper from "./step-wrapper.svelte";
import UniqueVisitors from "../stats/unique-visitors.svelte";

$: script = `<script async defer src="https://your-analytics.org/ya.js" data-domain="${$onboarding.url}"><\/script>`;

const copyScriptToClipboard = () => {
const textarea = document.getElementById('script') as HTMLTextAreaElement;
textarea.focus();
textarea.select();
document.execCommand('copy');
};
</script>

<StepWrapper>
<p>Add the following script to all pages you want to track on your website.</p>
<div class="relative mt-6">
<textarea id="script" value={script} rows="3" readonly class="w-full p-2 bg-gray-100 resize-none"></textarea>
<button on:click={copyScriptToClipboard}>
<svg class="absolute" style="top: 24px; right: 12px;" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
</button>
</div>
<p class="mt-6">Once added, please visit <a href="https://{$onboarding.url}" target="_blank" rel="noopener" class="text-pink-600 hover:underline">{$onboarding.url}</a> to see the first few visits logged below.</p>
<div class="relative mt-6 pb-6 flex justify-center">
<UniqueVisitors />
</div>
<p class="mt-6">Don't want to wait? <a href="/{$onboarding.url}" class="text-pink-600 hover:underline">Go to your dashboard.</a></p>
</StepWrapper>
22 changes: 22 additions & 0 deletions services/website/src/components/onboarding/step-header.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script lang="ts">
export let label: string;
export let stepNumber: number;
export let isUpcoming: boolean = false;
</script>

<style>
.isUpcoming div {
@apply border-gray-200;
}

.isUpcoming h3 {
@apply text-gray-500;
}
</style>

<li class:isUpcoming class="md:flex-1">
<div class="pl-4 py-2 block border-l-4 border-indigo-600 md:pl-0 md:pt-4 md:pb-0 md:border-l-0 md:border-t-4">
<h3 class="text-xs leading-4 text-indigo-600 font-semibold uppercase">Step {stepNumber}</h3>
<p class="text-sm leading-5 font-medium">{label}</p>
</div>
</li>
10 changes: 10 additions & 0 deletions services/website/src/components/onboarding/step-wrapper.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import { fly } from 'svelte/transition';
import Card from "../card.svelte";
</script>

<div in:fly={{x:1000, delay:500, duration:500}} out:fly={{x:-500, duration:500}}>
<Card clazz="p-4">
<slot />
</Card>
</div>
6 changes: 3 additions & 3 deletions services/website/src/components/timezone-select.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<script lang="ts">
import onboarding from "../stores/onboarding";

export let name: string;

/**
Expand Down Expand Up @@ -607,11 +609,9 @@
"US/Samoa": "−11:00",
"Etc/GMT+12": "−12:00"
};

let userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
</script>

<select required {name} bind:value={userTimezone} class="form-select w-full sm:text-sm sm:leading-5">
<select required {name} bind:value={$onboarding.timezone} class="form-select w-full sm:text-sm sm:leading-5">
{#each Object.entries(timezones) as [tzName, tzOffset]}
<option
value={tzName}>
Expand Down
Loading