spine-framework-portal 0.2.10 → 0.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ import { useState } from 'react'
2
2
  import { NavLink, useNavigate } from 'react-router-dom'
3
3
  import { Ticket, Users, BookOpen, GraduationCap, Store, LayoutGrid, User, LogOut, Save } from 'lucide-react'
4
4
  import { useAuth } from '@core/contexts/AuthContext'
5
- import { useCurrentApp } from '@core/contexts/AppContext'
5
+ import { useAppPath } from '@core/hooks/useAppPath'
6
6
  import { Button } from '@core/components/ui/button'
7
7
  import { Avatar, AvatarFallback } from '@core/components/ui/avatar'
8
8
  import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@core/components/ui/dialog'
@@ -20,14 +20,11 @@ const NAV_ITEMS = [
20
20
 
21
21
  export function PortalHeader() {
22
22
  const { user, logout } = useAuth()
23
- const app = useCurrentApp()
23
+ const appPath = useAppPath()
24
24
  const navigate = useNavigate()
25
25
  const [accountOpen, setAccountOpen] = useState(false)
26
26
  const [displayName, setDisplayName] = useState(user?.full_name || user?.email?.split('@')[0] || '')
27
27
 
28
- // Normalize route_prefix: '/' → '' so paths are /, /tickets, /kb, etc.
29
- const base = app.route_prefix === '/' ? '' : (app.route_prefix || '')
30
-
31
28
  const initials = (displayName || user?.email || 'U')
32
29
  .split(' ')
33
30
  .map((w: string) => w[0])
@@ -48,7 +45,7 @@ export function PortalHeader() {
48
45
  <>
49
46
  <header className="sticky top-0 z-50 bg-background border-b border-border shadow-sm">
50
47
  <div className="flex items-center h-14 px-6 gap-6">
51
- <NavLink to={`${base}/`} className="flex items-center gap-2 shrink-0 text-foreground hover:text-primary transition-colors">
48
+ <NavLink to={appPath('/')} className="flex items-center gap-2 shrink-0 text-foreground hover:text-primary transition-colors">
52
49
  <LayoutGrid size={18} className="text-primary" />
53
50
  <span className="font-semibold tracking-tight text-sm">Customer Portal</span>
54
51
  </NavLink>
@@ -57,7 +54,7 @@ export function PortalHeader() {
57
54
  {NAV_ITEMS.map(({ label, path, icon: Icon }) => (
58
55
  <NavLink
59
56
  key={path}
60
- to={`${base}${path}`}
57
+ to={appPath(path)}
61
58
  className={({ isActive }) =>
62
59
  [
63
60
  'flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors',
@@ -1,5 +1,5 @@
1
- import { createHandler } from '../../../.framework/functions/_shared/middleware'
2
- import { adminDb } from '../../../.framework/functions/_shared/db'
1
+ import { createHandler } from './_shared/middleware'
2
+ import { adminDb } from './_shared/db'
3
3
 
4
4
  /**
5
5
  * Portal Community Escalation Handler
@@ -1,5 +1,6 @@
1
- import { createHandler } from '../../../.framework/functions/_shared/middleware'
2
- import { processSignal } from '../../functions/custom_funnel-signal'
1
+ import { createHandler } from './_shared/middleware'
2
+ import { adminDb } from './_shared/db'
3
+ import { resolveTypeId } from './_shared/resolve-ids'
3
4
 
4
5
  export const handler = createHandler(async (ctx, body) => {
5
6
  const {
@@ -26,7 +27,21 @@ export const handler = createHandler(async (ctx, body) => {
26
27
  throw err
27
28
  }
28
29
 
29
- const payload = {
30
+ // Get the funnel_signal type_id
31
+ const { data: typeId } = await adminDb
32
+ .from('types')
33
+ .select('id')
34
+ .eq('kind', 'item')
35
+ .eq('slug', 'funnel_signal')
36
+ .single()
37
+
38
+ if (!typeId) {
39
+ const err: any = new Error('funnel_signal type not found')
40
+ err.statusCode = 500
41
+ throw err
42
+ }
43
+
44
+ const signalData = {
30
45
  account_id: ctx.accountId,
31
46
  person_id: ctx.principal.id,
32
47
  session_id: session_id || `portal_${ctx.principal.id}_${Date.now()}`,
@@ -43,13 +58,23 @@ export const handler = createHandler(async (ctx, body) => {
43
58
  occurred_at: new Date().toISOString()
44
59
  }
45
60
 
46
- const result = await processSignal(payload, { accountId: ctx.accountId, requestId: ctx.requestId }, {})
61
+ // Create the signal item directly using admin-data pattern
62
+ const { data: signal, error } = await adminDb
63
+ .from('items')
64
+ .insert({
65
+ type_id: typeId.id,
66
+ title: `${action_type} - ${action_value}`,
67
+ account_id: ctx.accountId,
68
+ data: signalData
69
+ })
70
+ .select('id')
71
+ .single()
47
72
 
48
- if (result?.status === 'error') {
49
- const err: any = new Error(result.error || 'Signal processing failed')
50
- err.statusCode = 400
73
+ if (error || !signal) {
74
+ const err: any = new Error(`Failed to create signal: ${error?.message || 'Unknown error'}`)
75
+ err.statusCode = 500
51
76
  throw err
52
77
  }
53
78
 
54
- return { status: 'ok', signal_id: result?.signal_id }
79
+ return { status: 'ok', signal_id: signal.id }
55
80
  })
package/index.tsx CHANGED
@@ -16,14 +16,6 @@ const DeveloperSettingsPage = lazy(() => import('./pages/DeveloperSettingsPage')
16
16
 
17
17
  function PortalLayout() {
18
18
  const location = useLocation()
19
- const app = useCurrentApp()
20
-
21
- // Normalize route_prefix: '/' → '' so paths are /, /tickets, /kb, etc.
22
- const base = app.route_prefix === '/' ? '' : (app.route_prefix || '')
23
- const prefixDepth = base.split('/').filter(Boolean).length
24
-
25
- const segments = location.pathname.split('/').filter(Boolean)
26
- const appSegments = segments.slice(prefixDepth) // strips the prefix segments
27
19
 
28
20
  return (
29
21
  <div className="h-full flex flex-col bg-background overflow-hidden">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spine-framework-portal",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "private": false,
5
5
  "description": "Customer Portal — self-service portal app for Spine Framework",
6
6
  "type": "module",