spine-framework-portal 0.1.5 → 0.1.7

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.
package/LICENSE.md ADDED
@@ -0,0 +1,13 @@
1
+ # Spine Framework Internal Use License 1.0.0
2
+
3
+ **Source-available. Free for internal use. Commercial rights reserved.**
4
+
5
+ Copyright © 2026 Dahl Ventures Inc. All rights reserved.
6
+
7
+ This software is licensed under the Spine Framework Internal Use License 1.0.0.
8
+
9
+ For full license terms, see: https://github.com/spine-framework/spine-framework/blob/main/LICENSE.md
10
+
11
+ **Summary:** You may use this software for internal business use only. Commercial use, resale, SaaS offering, white-labeling, and distribution require a separate Commercial License.
12
+
13
+ Contact: webmaster@spine-framework.com
package/README.md CHANGED
@@ -1,26 +1,58 @@
1
1
  # spine-framework-portal
2
2
 
3
- Customer self-service portal app for Spine Framework.
3
+ **We're building Spine in the open.** The source is visible, the roadmap is public, and we share what we learn along the way. That said, Spine is not open source — commercial use requires a license. See [LICENSE.md](./LICENSE.md) for details.
4
+
5
+ ---
6
+
7
+ Customer Portal is a self-service app for Spine Framework. It gives your customers a branded portal to submit and track support tickets, browse the knowledge base, access courses, and participate in community discussions.
8
+
9
+ ## Requirements
10
+
11
+ - [Spine Framework](https://www.npmjs.com/package/spine-framework) `>=0.1.0`
12
+ - A Spine project initialized with `npx spine-framework init`
4
13
 
5
14
  ## Installation
6
15
 
7
16
  ```bash
8
- npm install spine-framework-portal
17
+ npx spine-framework install-app spine-framework-portal@latest
9
18
  ```
10
19
 
11
- ## Usage
20
+ This seeds the required roles, types, and configuration into your database and registers the app with Spine's navigation system.
21
+
22
+ ## Features
23
+
24
+ - **Support tickets** — customers submit and track their own tickets
25
+ - **Knowledge base** — self-service article browsing and search
26
+ - **Courses** — lesson and course content delivery
27
+ - **Community** — discussion threads and Q&A
28
+ - **Marketplace** — app and integration discovery
29
+
30
+ ## Routes
31
+
32
+ | Path | Description |
33
+ |------|-------------|
34
+ | `/portal` | Portal home |
35
+ | `/portal/tickets` | Ticket list |
36
+ | `/portal/tickets/:id` | Ticket detail |
37
+ | `/portal/kb` | Knowledge base |
38
+ | `/portal/courses` | Courses |
39
+ | `/portal/community` | Community |
40
+ | `/portal/marketplace` | Marketplace |
41
+
42
+ ## Uninstalling
12
43
 
13
44
  ```bash
14
- npx spine-framework install-app customer-portal
45
+ npx spine-framework uninstall-app spine-framework-portal
15
46
  ```
16
47
 
17
- ## Features
48
+ ## License
18
49
 
19
- - Community discussion and Q&A
20
- - Course and lesson content delivery
21
- - Support ticket submission and tracking
22
- - Knowledge base article access
50
+ Spine Framework Portal is **source-available, free for internal use**. Commercial use requires a separate license.
23
51
 
24
- ## License
52
+ See [LICENSE.md](./LICENSE.md) for full terms.
53
+
54
+ For commercial licensing: [spine-framework.com](https://spine-framework.com) · webmaster@spine-framework.com
55
+
56
+ ---
25
57
 
26
- MIT
58
+ Copyright © 2026 Dahl Ventures Inc. All rights reserved.
@@ -2,6 +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
6
  import { Button } from '@core/components/ui/button'
6
7
  import { Avatar, AvatarFallback } from '@core/components/ui/avatar'
7
8
  import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@core/components/ui/dialog'
@@ -10,19 +11,23 @@ import { Label } from '@core/components/ui/label'
10
11
  import { Separator } from '@core/components/ui/separator'
11
12
 
12
13
  const NAV_ITEMS = [
13
- { label: 'Tickets', path: '/portal/tickets', icon: Ticket },
14
- { label: 'Knowledge Base', path: '/portal/kb', icon: BookOpen },
15
- { label: 'Courses', path: '/portal/courses', icon: GraduationCap },
16
- { label: 'Community', path: '/portal/community', icon: Users },
17
- { label: 'Marketplace', path: '/portal/marketplace', icon: Store },
14
+ { label: 'Tickets', path: '/tickets', icon: Ticket },
15
+ { label: 'Knowledge Base', path: '/kb', icon: BookOpen },
16
+ { label: 'Courses', path: '/courses', icon: GraduationCap },
17
+ { label: 'Community', path: '/community', icon: Users },
18
+ { label: 'Marketplace', path: '/marketplace', icon: Store },
18
19
  ]
19
20
 
20
21
  export function PortalHeader() {
21
22
  const { user, logout } = useAuth()
23
+ const app = useCurrentApp()
22
24
  const navigate = useNavigate()
23
25
  const [accountOpen, setAccountOpen] = useState(false)
24
26
  const [displayName, setDisplayName] = useState(user?.full_name || user?.email?.split('@')[0] || '')
25
27
 
28
+ // Normalize route_prefix: '/' → '' so paths are /, /tickets, /kb, etc.
29
+ const base = app.route_prefix === '/' ? '' : (app.route_prefix || '')
30
+
26
31
  const initials = (displayName || user?.email || 'U')
27
32
  .split(' ')
28
33
  .map((w: string) => w[0])
@@ -43,7 +48,7 @@ export function PortalHeader() {
43
48
  <>
44
49
  <header className="sticky top-0 z-50 bg-background border-b border-border shadow-sm">
45
50
  <div className="flex items-center h-14 px-6 gap-6">
46
- <NavLink to="/portal" className="flex items-center gap-2 shrink-0 text-foreground hover:text-primary transition-colors">
51
+ <NavLink to={`${base}/`} className="flex items-center gap-2 shrink-0 text-foreground hover:text-primary transition-colors">
47
52
  <LayoutGrid size={18} className="text-primary" />
48
53
  <span className="font-semibold tracking-tight text-sm">Customer Portal</span>
49
54
  </NavLink>
@@ -52,7 +57,7 @@ export function PortalHeader() {
52
57
  {NAV_ITEMS.map(({ label, path, icon: Icon }) => (
53
58
  <NavLink
54
59
  key={path}
55
- to={path}
60
+ to={`${base}${path}`}
56
61
  className={({ isActive }) =>
57
62
  [
58
63
  'flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors',
package/index.tsx CHANGED
@@ -1,9 +1,10 @@
1
1
  import { lazy, Suspense } from 'react'
2
- import { Routes, Route, Navigate } from 'react-router-dom'
2
+ import { Routes, Route, Navigate, useLocation } from 'react-router-dom'
3
3
  import { LoadingSpinner } from '@core/components/ui/LoadingSpinner'
4
4
  import { TooltipProvider } from '@core/components/ui/tooltip'
5
5
  import { PortalHeader } from './components/PortalHeader'
6
6
  import { PortalFooter } from './components/PortalFooter'
7
+ import { useCurrentApp } from '@core/contexts/AppContext'
7
8
 
8
9
  const HomePage = lazy(() => import('./pages/HomePage').then(m => ({ default: m.HomePage })))
9
10
  const TicketsPage = lazy(() => import('./pages/TicketsPage').then(m => ({ default: m.TicketsPage })))
@@ -13,6 +14,16 @@ const KnowledgePage = lazy(() => import('./pages/KnowledgePage').then(m => ({ de
13
14
  const MarketplacePage = lazy(() => import('./pages/MarketplacePage').then(m => ({ default: m.MarketplacePage })))
14
15
 
15
16
  function PortalLayout() {
17
+ const location = useLocation()
18
+ const app = useCurrentApp()
19
+
20
+ // Normalize route_prefix: '/' → '' so paths are /, /tickets, /kb, etc.
21
+ const base = app.route_prefix === '/' ? '' : (app.route_prefix || '')
22
+ const prefixDepth = base.split('/').filter(Boolean).length
23
+
24
+ const segments = location.pathname.split('/').filter(Boolean)
25
+ const appSegments = segments.slice(prefixDepth) // strips the prefix segments
26
+
16
27
  return (
17
28
  <div className="h-full flex flex-col bg-background overflow-hidden">
18
29
  <PortalHeader />
@@ -20,15 +31,15 @@ function PortalLayout() {
20
31
  <Suspense fallback={<div className="flex-1 flex items-center justify-center"><LoadingSpinner /></div>}>
21
32
  <div className="flex-1 flex flex-col min-h-0">
22
33
  <Routes>
23
- <Route path="/" element={<HomePage />} />
24
- <Route path="/tickets" element={<TicketsPage />} />
25
- <Route path="/tickets/:id" element={<TicketsPage />} />
26
- <Route path="/kb" element={<KnowledgePage />} />
27
- <Route path="/knowledge" element={<Navigate to="/portal/kb" replace />} />
28
- <Route path="/courses" element={<CoursesPage />} />
29
- <Route path="/community" element={<CommunityPage />} />
30
- <Route path="/marketplace" element={<MarketplacePage />} />
31
- <Route path="*" element={<Navigate to="/portal" replace />} />
34
+ <Route index element={<HomePage />} />
35
+ <Route path="tickets" element={<TicketsPage />} />
36
+ <Route path="tickets/:id" element={<TicketsPage />} />
37
+ <Route path="kb" element={<KnowledgePage />} />
38
+ <Route path="knowledge" element={<Navigate to="kb" replace />} />
39
+ <Route path="courses" element={<CoursesPage />} />
40
+ <Route path="community" element={<CommunityPage />} />
41
+ <Route path="marketplace" element={<MarketplacePage />} />
42
+ <Route path="*" element={<Navigate to="/" replace />} />
32
43
  </Routes>
33
44
  </div>
34
45
  </Suspense>
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "spine-framework-portal",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Customer Portal — self-service portal app for Spine Framework",
5
5
  "type": "module",
6
- "license": "MIT",
6
+ "license": "SEE LICENSE IN LICENSE.md",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/art-mojo-admin/spine",
9
+ "url": "https://github.com/spine-framework/portal",
10
10
  "directory": "custom/apps/customer-portal"
11
11
  },
12
12
  "peerDependencies": {
@@ -19,7 +19,8 @@
19
19
  "pages/",
20
20
  "components/",
21
21
  "functions/",
22
- "README.md"
22
+ "README.md",
23
+ "LICENSE.md"
23
24
  ],
24
25
  "spine": {
25
26
  "type": "app",