mirror of
https://fast.feibisi.com/https://github.com/parcelvoy/platform.git
synced 2025-09-01 12:26:08 +08:00
feat: show what links could send users into a journey
This commit is contained in:
parent
d89c9699a3
commit
36f3652dce
8 changed files with 79 additions and 9 deletions
|
@ -5,8 +5,8 @@ import { searchParamsSchema } from '../core/searchParams'
|
|||
import { JSONSchemaType, validate } from '../core/validate'
|
||||
import { extractQueryParams } from '../utilities'
|
||||
import Journey, { JourneyEntranceTriggerParams, JourneyParams } from './Journey'
|
||||
import { createJourney, getJourneyStepMap, getJourney, pagedJourneys, setJourneyStepMap, updateJourney, pagedEntrancesByJourney, getEntranceLog, pagedUsersByStep, archiveJourney, deleteJourney, exitUserFromJourney, publishJourney, skipDelayStep } from './JourneyRepository'
|
||||
import { JourneyStep, JourneyStepMapParams, journeyStepTypes, toJourneyStepMap } from './JourneyStep'
|
||||
import { createJourney, getJourney, pagedJourneys, setJourneyStepMap, updateJourney, pagedEntrancesByJourney, getEntranceLog, pagedUsersByStep, archiveJourney, deleteJourney, exitUserFromJourney, publishJourney, skipDelayStep, getJourneyStepMapForUI } from './JourneyRepository'
|
||||
import { JourneyStep, JourneyStepMapParams, journeyStepTypes } from './JourneyStep'
|
||||
import JourneyUserStep from './JourneyUserStep'
|
||||
import { User } from '../users/User'
|
||||
import { RequestError } from '../core/errors'
|
||||
|
@ -165,12 +165,13 @@ const journeyStepsParamsSchema: JSONSchemaType<JourneyStepMapParams> = {
|
|||
}
|
||||
|
||||
router.get('/:journeyId/steps', async ctx => {
|
||||
ctx.body = await getJourneyStepMap(ctx.state.journey!.id)
|
||||
console.log('run!')
|
||||
ctx.body = await getJourneyStepMapForUI(ctx.state.journey!)
|
||||
})
|
||||
|
||||
router.put('/:journeyId/steps', async ctx => {
|
||||
const { steps, children } = await setJourneyStepMap(ctx.state.journey!, validate(journeyStepsParamsSchema, ctx.request.body))
|
||||
ctx.body = await toJourneyStepMap(steps, children)
|
||||
await setJourneyStepMap(ctx.state.journey!, validate(journeyStepsParamsSchema, ctx.request.body))
|
||||
ctx.body = await getJourneyStepMapForUI(ctx.state.journey!)
|
||||
})
|
||||
|
||||
router.post('/:journeyId/duplicate', async ctx => {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Database } from '../config/database'
|
|||
import { RequestError } from '../core/errors'
|
||||
import { PageParams } from '../core/searchParams'
|
||||
import Journey, { JourneyParams, UpdateJourneyParams } from './Journey'
|
||||
import { JourneyStep, JourneyEntrance, JourneyStepMap, toJourneyStepMap, JourneyStepChild } from './JourneyStep'
|
||||
import { JourneyStep, JourneyEntrance, JourneyStepMap, toJourneyStepMap, JourneyStepChild, journeyStepTypes } from './JourneyStep'
|
||||
import JourneyUserStep from './JourneyUserStep'
|
||||
import { createTagSubquery, getTags, setTags } from '../tags/TagService'
|
||||
import { User } from '../users/User'
|
||||
|
@ -138,6 +138,17 @@ export const getJourneyStepChildren = async (journeyId: number, db?: Database):
|
|||
)
|
||||
}
|
||||
|
||||
export const getJourneyStepMapForUI = async (journey: Journey) => {
|
||||
const map = await getJourneyStepMap(journey.id)
|
||||
for (const key of Object.keys(map)) {
|
||||
const step = map[key]
|
||||
const type = journeyStepTypes[step.type]
|
||||
console.log('hydrate!', step.type)
|
||||
map[key] = await type.hydrate(step, journey)
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
export const getJourneyStepMap = async (journeyId: number) => {
|
||||
const [steps, children] = await Promise.all([
|
||||
getJourneySteps(journeyId),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { add, addDays, addHours, addMinutes, getDay, isEqual, isFuture, isPast, parse } from 'date-fns'
|
||||
import Model from '../core/Model'
|
||||
import { getCampaign, getCampaignSend, triggerCampaignSend } from '../campaigns/CampaignService'
|
||||
import { crossTimezoneCopy, random, snakeCase, uuid } from '../utilities'
|
||||
import { crossTimezoneCopy, pick, random, snakeCase, uuid } from '../utilities'
|
||||
import { Database } from '../config/database'
|
||||
import { compileTemplate } from '../render'
|
||||
import { logger } from '../config/logger'
|
||||
|
@ -14,6 +14,7 @@ import { JourneyState } from './JourneyState'
|
|||
import { EventPostJob, UserPatchJob } from '../jobs'
|
||||
import { exitUserFromJourney, getJourneyUserStepByExternalId } from './JourneyRepository'
|
||||
import JourneyUserStep from './JourneyUserStep'
|
||||
import Journey from './Journey'
|
||||
|
||||
export class JourneyStepChild extends Model {
|
||||
|
||||
|
@ -64,6 +65,11 @@ export class JourneyStep extends Model {
|
|||
async next(state: JourneyState): Promise<undefined | number> {
|
||||
return state.childrenOf(this.id)[0]?.child_id
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
static async hydrate(step: JourneyStepMapItem, journey: Journey): Promise<JourneyStepMapItem> {
|
||||
return Promise.resolve(step)
|
||||
}
|
||||
}
|
||||
|
||||
export class JourneyEntrance extends JourneyStep {
|
||||
|
@ -132,6 +138,24 @@ export class JourneyEntrance extends JourneyStep {
|
|||
y: 0,
|
||||
}, db)
|
||||
}
|
||||
|
||||
static async hydrate(step: JourneyStep, journey: Journey) {
|
||||
if (!step.data) return step
|
||||
if (step.data.trigger !== 'none') return step
|
||||
|
||||
const references = await Journey.all(
|
||||
qb => qb
|
||||
.where('journey_steps.type', 'link')
|
||||
.whereJsonPath('journey_steps.data', '$.target_id', '=', journey.id)
|
||||
.where('journeys.status', 'live')
|
||||
.whereNull('journeys.deleted_at')
|
||||
.whereNull('journeys.parent_id')
|
||||
.leftJoin('journey_steps', 'journey_steps.journey_id', '=', 'journeys.id')
|
||||
.select('journeys.name', 'journeys.id'),
|
||||
)
|
||||
step.data.references = references.map(item => pick(item, ['id', 'name']))
|
||||
return step
|
||||
}
|
||||
}
|
||||
|
||||
export class JourneyExit extends JourneyStep {
|
||||
|
@ -598,7 +622,7 @@ export const journeyStepTypes = [
|
|||
return a
|
||||
}, {})
|
||||
|
||||
interface JourneyStepMapItem {
|
||||
export interface JourneyStepMapItem {
|
||||
type: string
|
||||
name?: string
|
||||
data?: Record<string, unknown>
|
||||
|
|
|
@ -71,5 +71,4 @@ export default class ScheduledEntranceJob extends Job {
|
|||
await App.main.queue.enqueueBatch(items.map(({ id }) => JourneyProcessJob.from({ entrance_id: id })))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -127,6 +127,7 @@
|
|||
"entrance_multiple_entries": "Multiple Entries",
|
||||
"entrance_multiple_entries_desc": "Should people enter this journey multiple times?",
|
||||
"entrance_occurs": " occurs",
|
||||
"entrance_links": "Users can enter this journey by being sent from links in these journeys:",
|
||||
"entrance_simultaneous_entries": "Simultaneous Entries",
|
||||
"entrance_simultaneous_entries_desc": "If enabled, user could join this journey multiple times before finishing previous ones.",
|
||||
"entrance_trigger": "Trigger Entrance",
|
||||
|
|
|
@ -57,4 +57,10 @@
|
|||
.ui-tag.tiny .icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.ui-tag-list {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
|
@ -418,4 +418,14 @@
|
|||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.journey-step.entrance {
|
||||
max-width: 325px;
|
||||
}
|
||||
|
||||
.journey-step.entrance .references {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-direction: column;
|
||||
}
|
|
@ -16,6 +16,8 @@ import { env } from '../../../config/env'
|
|||
import { useTranslation, Trans } from 'react-i18next'
|
||||
import { createEventRule, isEventWrapper } from '../../users/rules/RuleHelpers'
|
||||
import { ruleDescription } from '../../users/rules/RuleDescriptions'
|
||||
import { Tag } from '../../../ui'
|
||||
import { Link } from 'react-router'
|
||||
|
||||
interface EntranceConfig {
|
||||
trigger: 'none' | 'event' | 'schedule'
|
||||
|
@ -29,6 +31,8 @@ interface EntranceConfig {
|
|||
// schedule based
|
||||
list_id?: number
|
||||
schedule?: string
|
||||
|
||||
references?: Array<{ id: number, name: string }>
|
||||
}
|
||||
|
||||
const triggerOptions = [
|
||||
|
@ -79,6 +83,7 @@ export const entranceStep: JourneyStepType<EntranceConfig> = {
|
|||
rule,
|
||||
list_id,
|
||||
schedule,
|
||||
references = [],
|
||||
},
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
|
@ -138,6 +143,19 @@ export const entranceStep: JourneyStepType<EntranceConfig> = {
|
|||
)
|
||||
}
|
||||
|
||||
if (references.length) {
|
||||
return <div className="references">
|
||||
<span>{t('entrance_links')}</span>
|
||||
<div className="ui-tag-list">
|
||||
{references.map(journey =>
|
||||
<Tag variant="plain" key={journey.id}>
|
||||
<Link to={`../journeys/${journey.id}`}>{journey.name}</Link>
|
||||
</Tag>,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
return <>{t('entrance_empty')}</>
|
||||
},
|
||||
Edit({ onChange, project: { id: projectId }, journey: { id: journeyId }, stepId, value }) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue