Skip to content

Instantly share code, notes, and snippets.

@joelhooks
Forked from vojtaholik/offer.ts
Created March 2, 2023 18:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joelhooks/e6315053ef3259771ad65c4cbbf3d861 to your computer and use it in GitHub Desktop.
Save joelhooks/e6315053ef3259771ad65c4cbbf3d861 to your computer and use it in GitHub Desktop.
import {publicProcedure, router} from '@skillrecordings/skill-lesson'
import {isEmpty} from 'lodash'
import {getToken} from 'next-auth/jwt'
import {getSubscriberFromCookie} from '@skillrecordings/skill-lesson/utils/ck-subscriber-from-cookie'
export const offerRouter = router({
getNextOffer: publicProcedure.query(async ({ctx}) => {
const token = await getToken({req: ctx.req})
const subscriber = await getSubscriberFromCookie(ctx.req)
console.log({subscriber})
if (subscriber && subscriber.fields) {
// User hasn't started any tutorials
// - If the user hasn't started a tutorial, display an attractive and eye catching "banner" for the TypeScript Beginner's tutorial
const HAS_NOT_STARTED_ANY_TUTORIAL = checkStartedKeysNull(
subscriber.fields,
)
console.log({HAS_NOT_STARTED_ANY_TUTORIAL})
// User has started a tutorial
// - Show them a "continue" variant of the above
const startedTutorials = orderStartedByDate(subscriber.fields)
const HAS_STARTED_TUTORIAL = !isEmpty(startedTutorials)
console.log({HAS_STARTED_TUTORIAL})
// User has finished the Basics Tutorial
// - Show them a CTA for the Zod tutorial, show continue as appropriate
const HAS_COMPLETED_BEGINNERS_TYPESCRIPT_TUTORIAL =
hasCompletedBeginnersTypeScriptTutorial(subscriber.fields)
console.log({HAS_COMPLETED_BEGINNERS_TYPESCRIPT_TUTORIAL})
// User has finished both tutorials and hasn't bought the Core Volume
// - Show a CTA for the core volume
const completedTutorials = getCompletedTutorials(subscriber.fields)
const HAS_COMPLETED_TWO_OR_MORE_TUTORIALS = completedTutorials.length > 1
console.log({HAS_COMPLETED_TWO_OR_MORE_TUTORIALS})
// User has purchased Core Volume, but hasn't started yet
// - Show them a get started CTA
const HAS_PURCHASED_CORE_VOLUME = hasPurchasedCoreVolume(
subscriber.fields,
)
console.log({HAS_PURCHASED_CORE_VOLUME})
// User has purchased the Core Volume and made progress
// - Show them a "continue" CTA for the next step
if (HAS_PURCHASED_CORE_VOLUME && token && token.sub) {
// TODO: Ideally we would call a "product progress" here
}
const getOffer = () => {
let offer = {}
switch (true) {
case HAS_NOT_STARTED_ANY_TUTORIAL:
offer = {
title: "Beginner's TypeScript",
path: '/tutorials/beginners-typescript',
cta: 'Get started with TypeScript',
}
break
case HAS_STARTED_TUTORIAL:
offer = {
title: "Beginner's TypeScript",
path: '/tutorials/beginners-typescript',
cta: 'Continue learning',
}
break
case HAS_COMPLETED_BEGINNERS_TYPESCRIPT_TUTORIAL:
offer = {
title:
'Zod Tutorial — TypeScript-first schema declaration and validation library',
path: '/tutorials/zod',
cta: 'Learn Zod',
}
break
case HAS_COMPLETED_TWO_OR_MORE_TUTORIALS:
offer = {
title: 'Professional TypeScript Workshops',
path: '/workshops',
cta: 'Become a TypeScript Wizard',
}
break
case HAS_PURCHASED_CORE_VOLUME:
offer = {}
break
default:
offer = {}
}
return offer
}
const offer = getOffer()
return {
offer,
startedTutorials,
HAS_STARTED_TUTORIAL,
completedBeginnersTypeScriptTutorial:
HAS_COMPLETED_BEGINNERS_TYPESCRIPT_TUTORIAL,
completedTwoOrMoreTutorials: HAS_COMPLETED_TWO_OR_MORE_TUTORIALS,
purchasedCoreVolume: HAS_PURCHASED_CORE_VOLUME,
}
}
}),
})
type Fields = {[key: string]: string | null}
function checkStartedKeysNull(fields: Fields) {
for (const key in fields) {
if (key.startsWith('started_') && fields[key] !== null) {
return false
}
}
return true
}
function orderStartedByDate(obj: Fields): Fields {
const startedKeys = Object.keys(obj)
.filter((key: string) => key.startsWith('started_'))
.filter((key: string) => obj[key] !== null)
const sortedKeys = startedKeys.sort((a: string, b: string) => {
const dateA = obj[a]
const dateB = obj[b]
if (dateA && dateB && Date.parse(dateA) && Date.parse(dateB)) {
return new Date(dateB).getTime() - new Date(dateA).getTime()
}
return 0
})
const sortedObj: Fields = {}
sortedKeys.forEach((key) => (sortedObj[key] = obj[key]))
return sortedObj
}
function hasCompletedBeginnersTypeScriptTutorial(fields: Fields): boolean {
for (let key in fields) {
if (
key.includes('completed_on') &&
key.includes('beginners_typescript') &&
fields[key] !== null
) {
return true
}
}
return false
}
function getCompletedTutorials(fields: Fields) {
const completedOnItems = []
for (let key in fields) {
if (key.includes('completed_on')) {
completedOnItems.push(key)
}
}
return completedOnItems
}
function hasPurchasedCoreVolume(fields: Fields) {
for (let key in fields) {
if (key.includes('purchased_core_volume_on')) {
return true
}
}
return false
}
// Testing data
const fields = {
devs_on_team: 'skip',
do_not_survey: 'true',
is_developer: null,
job_title: null,
last_name: 'Holik',
last_surveyed_on: '2023-01-05 15:41:00 GMT+0',
level: 'advanced-beginner',
purchased_core_volume_on: null,
purchased_workshop_series_01: null,
read_dont_use_function_keyword_in_typescript_on: '2022-12-14',
read_mental_model_for_typescript_generics_on: null,
read_rewriting_typescript_in_rust_on: null,
read_structure_of_a_typescript_error_on: null,
read_when_should_you_use_zod_on: null,
read_writing_string_replace_in_typescript_on: '2022-12-14',
started_beginners_type_script_tutorial: '2023-02-15',
started_type_transformations_workshop: null,
started_zod_tutorial: '2022-10-07',
ts_at_work: 'true',
welcomed_on: null,
years_developer: null,
}
const completedBeginnersTypeScriptTutorial = {
started_beginners_type_script_tutorial: '2023-02-15',
}
const notCompletedBeginnersTypeScriptTutorial = {
started_beginners_type_script_tutorial: null,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment