Files
zeekayandHanzo Dev 841ea1c0da no hogs: products and insights
716 files rewritten, 30 renamed, same family mapping as the frontend pass.

Held back, and each for a reason rather than by oversight:

  db_table values          insights_hogfunction, insights_hogflow,
                           insights_hogfunctiontemplate, hogflow_templates.
                           Renaming a table is ALTER TABLE, so it ships as a
                           migration with the raw SQL in nodejs that names it.
  hog_invocation_results   a Datastore table with Kafka engine tables on it
  products/review_hog      the module path is the Django app path; moving it
                           moves app_label and every migration is recorded under
                           (app_label, name). That is the same history break as
                           renaming a migration file, one level up.
  */migrations/*           five files, untouched
  whogho                   an integration vendor

Checked: 395 of 397 changed Python files compile. The two that do not —
insightsql/database/database.py and temporal/schedule.py — fail identically at
origin/main under the same interpreter, because they use `type X = ...` and
`except*`, which need the 3.13 this repo pins and not the 3.9 doing the check.
Running the check against the original is what told those apart from a real
break.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-09 09:16:43 -07:00

289 lines
9.0 KiB
TypeScript

import { MakeLogicType, actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea'
import { forms } from 'kea-forms'
import type { DeepPartial, DeepPartialMap, FieldName, ValidationErrorType } from 'kea-forms'
import { loaders } from 'kea-loaders'
import { router, urlToAction } from 'kea-router'
import { toast } from '@hanzo/elements'
import api from 'lib/api'
import { teamLogic } from 'scenes/teamLogic'
import { urls } from 'scenes/urls'
import { deleteFromTree } from '~/layout/panel-layout/ProjectTree/projectTreeLogic'
import type { LinkType } from '~/types'
import { Breadcrumb, ProjectTreeRef } from '~/types'
import { linksLogic } from './linksLogic'
export type AvailableDomain = 'pscript.gg' | 'postho.gg' | 'script.gg' | 'custom'
export type DomainDefinition = {
label: string
value: AvailableDomain
soon?: boolean
paid?: boolean
}
export const AVAILABLE_DOMAINS: DomainDefinition[] = [
{ label: 'pscript.gg', value: 'pscript.gg' },
{ label: 'postho.gg', value: 'postho.gg', soon: true },
{ label: 'script.gg', value: 'script.gg', soon: true },
{ label: 'Custom (BYOD)', value: 'custom', soon: true, paid: true },
]
export const DEFAULT_SHORT_LINK_DOMAIN = 'pscript.gg'
export const NEW_LINK: Partial<LinkType> = {
id: 'new',
short_link_domain: DEFAULT_SHORT_LINK_DOMAIN,
short_code: '',
redirect_url: '',
description: '',
}
export interface LinkLogicProps {
/** Either a UUID or "new". */
id: string
}
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
export interface linkLogicValues {
links: LinkType[] // linksLogic
currentTeamId: number | null // teamLogic
breadcrumbs: Breadcrumb[]
isEditingLink: boolean
isLinkSubmitting: boolean
isLinkValid: boolean
link: LinkType
linkAllErrors: Record<string, any>
linkChanged: boolean
linkErrors: DeepPartialMap<LinkType, ValidationErrorType>
linkHasErrors: boolean
linkLoading: boolean
linkManualErrors: Record<string, any>
linkMissing: boolean
linkTouched: boolean
linkTouches: Record<string, boolean>
linkValidationErrors: DeepPartialMap<LinkType, ValidationErrorType>
mode: 'edit' | 'view'
projectTreeRef: ProjectTreeRef
showLinkErrors: boolean
}
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
export interface linkLogicActions {
loadLinks: () => any // linksLogic
loadLinksSuccess: (
links: LinkType[],
payload?: any
) => {
links: LinkType[]
payload?: any
} // linksLogic
deleteLink: (linkId: LinkType['id']) => {
linkId: string
}
editLink: (editing: boolean) => {
editing: boolean
}
loadLink: () => any
loadLinkFailure: (
error: string,
errorObject?: any
) => {
error: string
errorObject?: any
}
loadLinkSuccess: (
link: LinkType,
payload?: any
) => {
link: LinkType
payload?: any
}
resetLink: (values?: LinkType) => {
values?: LinkType
}
saveLink: (updatedLink: Partial<LinkType>) => Partial<LinkType>
saveLinkFailure: (
error: string,
errorObject?: any
) => {
error: string
errorObject?: any
}
saveLinkSuccess: (
link: LinkType,
payload?: Partial<LinkType>
) => {
link: LinkType
payload?: Partial<LinkType>
}
setLinkManualErrors: (errors: Record<string, any>) => {
errors: Record<string, any>
}
setLinkMissing: () => {
value: true
}
setLinkValue: (
key: FieldName,
value: any
) => {
name: FieldName
value: any
}
setLinkValues: (values: DeepPartial<LinkType>) => {
values: DeepPartial<LinkType>
}
submitLink: () => {
value: boolean
}
submitLinkFailure: (
error: Error,
errors: Record<string, any>
) => {
error: Error
errors: Record<string, any>
}
submitLinkRequest: (link: LinkType) => {
link: LinkType
}
submitLinkSuccess: (link: LinkType) => {
link: LinkType
}
touchLinkField: (key: string) => {
key: string
}
}
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
export interface linkLogicMeta {
key: string
__keaTypeGenInternalSelectorTypes: {
mode: (id: string) => 'edit' | 'view'
breadcrumbs: (link: LinkType) => Breadcrumb[]
projectTreeRef: (arg: string) => ProjectTreeRef
}
}
export type linkLogicType = MakeLogicType<linkLogicValues, linkLogicActions, LinkLogicProps, linkLogicMeta>
export const linkLogic = kea<linkLogicType>([
path(['products', 'links', 'frontend', 'linkLogic']),
props({} as LinkLogicProps),
key(({ id }) => id),
connect(() => ({
values: [teamLogic, ['currentTeamId'], linksLogic, ['links']],
actions: [linksLogic, ['loadLinks', 'loadLinksSuccess']],
})),
actions({
setLinkMissing: true,
editLink: (editing: boolean) => ({ editing }),
deleteLink: (linkId: LinkType['id']) => ({ linkId }),
}),
loaders(({ props, actions }) => ({
link: {
loadLink: async () => {
if (props.id && props.id !== 'new') {
try {
const response = await api.links.get(props.id)
return response
} catch (error) {
actions.setLinkMissing()
throw error
}
}
return NEW_LINK as LinkType
},
saveLink: async (updatedLink: Partial<LinkType>) => {
const result: LinkType = await (props.id === 'new'
? api.links.create(updatedLink)
: api.links.update(props.id, updatedLink))
if (props.id === 'new') {
router.actions.replace(urls.link(result.id))
}
return result
},
},
})),
forms(({ actions, props }) => ({
link: {
defaults: { ...NEW_LINK } as LinkType,
errors: (payload) => ({
redirect_url: !payload?.redirect_url ? 'Must include a destination url' : undefined,
short_code: !payload?.short_code ? 'Must include a short code' : undefined,
}),
submit: async (payload) => {
if (props.id && props.id !== 'new') {
actions.saveLink(payload)
} else {
actions.saveLink({ ...payload, _create_in_folder: 'Unfiled/Links' })
}
},
},
})),
reducers({
linkMissing: [false, { setLinkMissing: () => true }],
isEditingLink: [false, { editLink: (_, { editing }) => editing }],
}),
selectors({
mode: [(_, p) => [p.id], (id: string): 'view' | 'edit' => (id === 'new' ? 'edit' : 'view')],
breadcrumbs: [
(s) => [s.link],
(link: LinkType): Breadcrumb[] => [
{
key: 'Link',
name: 'Link Management',
path: urls.links(),
iconType: 'link',
},
{
key: ['Link', link.id || 'new'],
name: `${link.short_code} (${link.redirect_url})`,
iconType: 'link',
},
],
],
projectTreeRef: [
() => [(_, props: LinkLogicProps) => props.id],
(id: string): ProjectTreeRef => ({ type: 'link', ref: id === 'new' ? null : String(id) }),
],
}),
listeners(({ actions, values }) => ({
saveLinkSuccess: ({ link }) => {
toast.success('Link saved')
actions.loadLinks()
link.id && router.actions.replace(urls.link(link.id))
actions.editLink(false)
},
deleteLink: async ({ linkId }) => {
try {
await api.links.delete(linkId)
toast.info('Link deleted. Existing `$linkclick` events will be kept for future analysis')
actions.loadLinksSuccess(values.links.filter((link) => link.id !== linkId))
deleteFromTree('link', linkId)
router.actions.push(urls.links())
} catch (e) {
toast.error(`Error deleting Link: ${e}`)
}
},
})),
urlToAction(({ actions, props }) => ({
[urls.link(props.id ?? 'new')]: (_, __, ___, { method }) => {
// If the URL was pushed (user clicked on a link), reset the scene's data.
// This avoids resetting form fields if you click back/forward.
if (method === 'PUSH') {
if (props.id) {
actions.loadLink()
}
}
},
})),
afterMount(({ props, actions }) => {
if (props.id !== 'new') {
actions.loadLink()
}
}),
])