@akinon/ui-shell-dev 1.1.0 → 1.2.0
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/dist/akinon-favicon.png +0 -0
- package/dist/akinon-logo.png +0 -0
- package/dist/akinon-logo.svg +10 -0
- package/dist/assets/index-BFs1gfY5.css +1 -0
- package/dist/assets/index-Dk6Gr15i.js +408 -0
- package/dist/assets/jost-cyrillic-wght-normal-B0_vFdaS.woff2 +0 -0
- package/dist/assets/jost-latin-ext-wght-normal-B4kqP43q.woff2 +0 -0
- package/dist/assets/jost-latin-wght-normal-CfFW3YMY.woff2 +0 -0
- package/dist/index.html +2 -2
- package/dist/logo.png +0 -0
- package/dist/symbol.png +0 -0
- package/package.json +7 -3
- package/public/akinon-favicon.png +0 -0
- package/public/akinon-logo.png +0 -0
- package/public/akinon-logo.svg +10 -0
- package/public/logo.png +0 -0
- package/public/symbol.png +0 -0
- package/src/components/AppLayout.tsx +8 -26
- package/src/components/Header.tsx +20 -11
- package/src/components/Navigation.tsx +37 -64
- package/src/components/PageContent.tsx +9 -0
- package/src/components/PageHeading.tsx +61 -0
- package/src/components/PluginTestPage.scss +26 -0
- package/src/components/PluginTestPage.tsx +89 -207
- package/src/components/WelcomePage.tsx +72 -69
- package/src/components/layout.scss +26 -24
- package/src/global.scss +33 -0
- package/src/main.tsx +8 -2
- package/src/pages/Dashboard.tsx +25 -0
- package/src/pages/External.tsx +15 -0
- package/src/pages/PluginTest.tsx +7 -0
- package/src/pages/pages.scss +9 -0
- package/src/providers.tsx +108 -0
- package/src/routes.tsx +44 -0
- package/src/server/dev-server.mjs +18 -15
- package/src/shell-app.tsx +12 -3
- package/src/utils/navigation.tsx +65 -0
- package/dist/assets/index-cI-pE1VB.js +0 -387
- package/dist/assets/index-tRJwBFdH.css +0 -1
- package/src/components/AppRouter.tsx +0 -41
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { ApplicationActions, RegisteredApp } from '@akinon/app-shell';
|
|
2
|
+
import { AppShellProvider, type ShellNavigation } from '@akinon/app-shell';
|
|
3
|
+
import { notification } from '@akinon/ui-react';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { useNavigate, useSearchParams } from 'react-router-dom';
|
|
6
|
+
|
|
7
|
+
import type { ShellConfig } from './types';
|
|
8
|
+
|
|
9
|
+
interface ProvidersProps {
|
|
10
|
+
config: ShellConfig;
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const Providers: React.FC<ProvidersProps> = ({ config, children }) => {
|
|
15
|
+
const [api, contextHolder] = notification.useNotification();
|
|
16
|
+
const [searchParams] = useSearchParams();
|
|
17
|
+
const navigate = useNavigate();
|
|
18
|
+
|
|
19
|
+
// Convert shell config to apps format for AppShellProvider
|
|
20
|
+
const apps: RegisteredApp[] = [
|
|
21
|
+
{
|
|
22
|
+
id: 1,
|
|
23
|
+
url: config.plugin.url,
|
|
24
|
+
slug: config.plugin.name.toLowerCase().replace(/\s+/g, '-'),
|
|
25
|
+
path: '', // Empty path for development like playground
|
|
26
|
+
type: config.plugin.type === 'fullpage' ? 'full_page' : 'plugin',
|
|
27
|
+
name: config.plugin.name
|
|
28
|
+
}
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const navigation: ShellNavigation = {
|
|
32
|
+
navigate: ({ id, path, external }: any) => {
|
|
33
|
+
const app = apps.find(app => app.id === id);
|
|
34
|
+
if (!app) return;
|
|
35
|
+
|
|
36
|
+
const uri = external ? path : `/external/${app.slug}${path || ''}`;
|
|
37
|
+
navigate(uri);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const data = {
|
|
42
|
+
lng: 'en',
|
|
43
|
+
username: 'Developer',
|
|
44
|
+
searchParams: Object.fromEntries([...searchParams])
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const defaultActions = {
|
|
48
|
+
showModalDialog: () => {
|
|
49
|
+
// Implementation for showing a modal dialog
|
|
50
|
+
},
|
|
51
|
+
showToast: (
|
|
52
|
+
content: string,
|
|
53
|
+
type: 'success' | 'error' | 'info' | 'warning' | 'loading' | 'destroy'
|
|
54
|
+
) => {
|
|
55
|
+
if (type === 'loading' || type === 'destroy') {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
api[type]({
|
|
59
|
+
message: content,
|
|
60
|
+
placement: 'top'
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
showErrorMessage: (title: string, content: string) => {
|
|
64
|
+
api.error({
|
|
65
|
+
message: title,
|
|
66
|
+
description: content,
|
|
67
|
+
placement: 'top'
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
showRichModal: () => {
|
|
71
|
+
// Rich modal implementation
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const customActions = {
|
|
76
|
+
reloadPage: () => window.location.reload(),
|
|
77
|
+
logMessage: (newItem: Object) => {
|
|
78
|
+
try {
|
|
79
|
+
const prevItems = JSON.parse(
|
|
80
|
+
localStorage.getItem('logMessages') || '[]'
|
|
81
|
+
);
|
|
82
|
+
localStorage.setItem(
|
|
83
|
+
'logMessages',
|
|
84
|
+
JSON.stringify([...prevItems, newItem])
|
|
85
|
+
);
|
|
86
|
+
} catch {}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const actions: ApplicationActions = {
|
|
91
|
+
...defaultActions,
|
|
92
|
+
actions: {
|
|
93
|
+
...customActions
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<AppShellProvider
|
|
99
|
+
apps={apps}
|
|
100
|
+
navigation={navigation}
|
|
101
|
+
data={data}
|
|
102
|
+
actions={actions}
|
|
103
|
+
>
|
|
104
|
+
{children}
|
|
105
|
+
{contextHolder}
|
|
106
|
+
</AppShellProvider>
|
|
107
|
+
);
|
|
108
|
+
};
|
package/src/routes.tsx
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useAppShell } from '@akinon/app-shell';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Route, Routes as ReactRouterRoutes } from 'react-router-dom';
|
|
4
|
+
|
|
5
|
+
import { PageDashboard } from './pages/Dashboard';
|
|
6
|
+
import { PageExternal } from './pages/External';
|
|
7
|
+
import { PagePluginTest } from './pages/PluginTest';
|
|
8
|
+
|
|
9
|
+
export const Routes = () => {
|
|
10
|
+
const { apps, configs } = useAppShell();
|
|
11
|
+
const fullpageApps = apps.filter(app => app.type === 'full_page');
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<ReactRouterRoutes>
|
|
15
|
+
{fullpageApps.map(app => (
|
|
16
|
+
<Route key={app.id} path={`/external/${app.slug}`}>
|
|
17
|
+
<Route index element={<PageExternal id={app.id} path={app.path} />} />
|
|
18
|
+
|
|
19
|
+
{(configs.get(app.id) as any)?.menu?.map((sub: any) => {
|
|
20
|
+
return (
|
|
21
|
+
sub.path !== '/' && (
|
|
22
|
+
<Route
|
|
23
|
+
key={sub.path}
|
|
24
|
+
path={`/external/${app.slug}${sub.path}`}
|
|
25
|
+
element={
|
|
26
|
+
<PageExternal
|
|
27
|
+
id={app.id}
|
|
28
|
+
path={
|
|
29
|
+
app.subSlug ? `${app.subSlug}${sub.path}` : sub.path
|
|
30
|
+
}
|
|
31
|
+
/>
|
|
32
|
+
}
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
35
|
+
);
|
|
36
|
+
})}
|
|
37
|
+
</Route>
|
|
38
|
+
))}
|
|
39
|
+
|
|
40
|
+
<Route path="/plugin-test" element={<PagePluginTest />} />
|
|
41
|
+
<Route path="/" element={<PageDashboard />} />
|
|
42
|
+
</ReactRouterRoutes>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
@@ -35,12 +35,13 @@ export class ShellDevServer {
|
|
|
35
35
|
async start() {
|
|
36
36
|
try {
|
|
37
37
|
console.log('🔧 Starting shell server initialization...');
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
const shellRoot = path.resolve(__dirname, '../..');
|
|
40
40
|
const distPath = path.resolve(shellRoot, 'dist');
|
|
41
41
|
// Only use production mode if explicitly set via env var
|
|
42
|
-
const isProduction =
|
|
43
|
-
|
|
42
|
+
const isProduction =
|
|
43
|
+
process.env.NODE_ENV === 'production' && fs.existsSync(distPath);
|
|
44
|
+
|
|
44
45
|
console.log(`📁 Shell root: ${shellRoot}`);
|
|
45
46
|
console.log(`🏭 Production mode: ${isProduction}`);
|
|
46
47
|
|
|
@@ -50,14 +51,14 @@ export class ShellDevServer {
|
|
|
50
51
|
if (isProduction) {
|
|
51
52
|
// Production: serve built assets
|
|
52
53
|
this.app.use('/assets', express.static(path.join(distPath, 'assets')));
|
|
53
|
-
|
|
54
|
+
|
|
54
55
|
this.app.get('*', (req, res, next) => {
|
|
55
56
|
if (req.path.startsWith('/api')) {
|
|
56
57
|
return next();
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
console.log(`📄 Serving production HTML for: ${req.url}`);
|
|
60
|
-
|
|
61
|
+
|
|
61
62
|
let template = fs.readFileSync(
|
|
62
63
|
path.resolve(distPath, 'index.html'),
|
|
63
64
|
'utf-8'
|
|
@@ -85,20 +86,18 @@ export class ShellDevServer {
|
|
|
85
86
|
|
|
86
87
|
console.log('✅ Vite server created successfully');
|
|
87
88
|
|
|
88
|
-
//
|
|
89
|
-
this.app.use(this.viteServer.middlewares);
|
|
90
|
-
|
|
91
|
-
console.log('✅ Vite middleware configured');
|
|
92
|
-
|
|
93
|
-
// Serve the shell app (only for HTML requests)
|
|
89
|
+
// Serve the shell app (only for HTML requests) - MUST be before Vite middleware
|
|
94
90
|
this.app.get('*', async (req, res, next) => {
|
|
95
|
-
if (
|
|
91
|
+
if (
|
|
92
|
+
req.path.startsWith('/api') ||
|
|
93
|
+
req.path.startsWith('/src') ||
|
|
94
|
+
req.path.startsWith('/@') ||
|
|
95
|
+
req.path.includes('.')
|
|
96
|
+
) {
|
|
96
97
|
return next();
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
try {
|
|
100
|
-
console.log(`📄 Serving development HTML for: ${req.url}`);
|
|
101
|
-
|
|
102
101
|
let template = fs.readFileSync(
|
|
103
102
|
path.resolve(__dirname, '../../index.html'),
|
|
104
103
|
'utf-8'
|
|
@@ -115,7 +114,6 @@ export class ShellDevServer {
|
|
|
115
114
|
`<script>window.__SHELL_CONFIG__ = ${JSON.stringify(this.config)};</script>`
|
|
116
115
|
);
|
|
117
116
|
|
|
118
|
-
console.log(`✅ HTML processed successfully for ${req.url}`);
|
|
119
117
|
res.status(200).set({ 'Content-Type': 'text/html' }).end(template);
|
|
120
118
|
} catch (error) {
|
|
121
119
|
console.error(`❌ Error serving ${req.url}:`, error);
|
|
@@ -123,6 +121,11 @@ export class ShellDevServer {
|
|
|
123
121
|
next(error);
|
|
124
122
|
}
|
|
125
123
|
});
|
|
124
|
+
|
|
125
|
+
// Use Vite middleware for assets and HMR - MUST be after our route handler
|
|
126
|
+
this.app.use(this.viteServer.middlewares);
|
|
127
|
+
|
|
128
|
+
console.log('✅ Vite middleware configured');
|
|
126
129
|
}
|
|
127
130
|
|
|
128
131
|
// Start server
|
package/src/shell-app.tsx
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { AkinonUiProvider } from '@akinon/ui-react';
|
|
2
2
|
import React from 'react';
|
|
3
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
3
4
|
|
|
4
5
|
import { AppLayout } from './components/AppLayout';
|
|
6
|
+
import { Providers } from './providers';
|
|
7
|
+
import { Routes } from './routes';
|
|
5
8
|
import type { ShellConfig } from './types';
|
|
6
9
|
|
|
7
10
|
interface ShellAppProps {
|
|
@@ -10,8 +13,14 @@ interface ShellAppProps {
|
|
|
10
13
|
|
|
11
14
|
export const ShellApp: React.FC<ShellAppProps> = ({ config }) => {
|
|
12
15
|
return (
|
|
13
|
-
<
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
+
<BrowserRouter>
|
|
17
|
+
<AkinonUiProvider>
|
|
18
|
+
<Providers config={config}>
|
|
19
|
+
<AppLayout>
|
|
20
|
+
<Routes />
|
|
21
|
+
</AppLayout>
|
|
22
|
+
</Providers>
|
|
23
|
+
</AkinonUiProvider>
|
|
24
|
+
</BrowserRouter>
|
|
16
25
|
);
|
|
17
26
|
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { RegisteredApp } from '@akinon/app-shell';
|
|
2
|
+
import type { TMenuItemType } from '@akinon/ui-react';
|
|
3
|
+
|
|
4
|
+
const STATIC_NAVIGATION_ITEMS: TMenuItemType[] = [
|
|
5
|
+
{
|
|
6
|
+
key: '/',
|
|
7
|
+
label: 'Welcome',
|
|
8
|
+
icon: 'dashboard'
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
key: '/placeholder-1',
|
|
12
|
+
label: 'Settings',
|
|
13
|
+
icon: 'ayarlar',
|
|
14
|
+
disabled: true
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
key: '/placeholder-2',
|
|
18
|
+
label: 'Help & Support',
|
|
19
|
+
icon: 'bilgilendirme_servis',
|
|
20
|
+
disabled: true
|
|
21
|
+
}
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const PLUGIN_NAVIGATION_ITEMS: TMenuItemType[] = [
|
|
25
|
+
{
|
|
26
|
+
key: '/plugin-test',
|
|
27
|
+
label: 'Plugin Test',
|
|
28
|
+
icon: 'box'
|
|
29
|
+
}
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
export const getStaticNavigationItems = () => {
|
|
33
|
+
return STATIC_NAVIGATION_ITEMS;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const getFullpageNavigationItems = (
|
|
37
|
+
apps: RegisteredApp[],
|
|
38
|
+
configs: Map<number, any>
|
|
39
|
+
): TMenuItemType[] => {
|
|
40
|
+
return apps
|
|
41
|
+
.filter(app => app.type === 'full_page')
|
|
42
|
+
.map((app): TMenuItemType => {
|
|
43
|
+
return {
|
|
44
|
+
key: `/external/${app.slug}`,
|
|
45
|
+
label: app.name,
|
|
46
|
+
icon: 'stoklistesi',
|
|
47
|
+
children: configs
|
|
48
|
+
.get(app.id)
|
|
49
|
+
?.menu?.map((sub: any): TMenuItemType | false => {
|
|
50
|
+
if (sub?.child) return false;
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
key: `/external/${app.slug}${sub.path}`,
|
|
54
|
+
label: sub.label,
|
|
55
|
+
icon: 'eksi'
|
|
56
|
+
};
|
|
57
|
+
})
|
|
58
|
+
.filter(Boolean)
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const getPluginNavigationItems = () => {
|
|
64
|
+
return PLUGIN_NAVIGATION_ITEMS;
|
|
65
|
+
};
|