@akinon/ui-shell-dev 1.5.3 → 1.5.4

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/index.html CHANGED
@@ -24,8 +24,8 @@
24
24
  box-sizing: border-box;
25
25
  }
26
26
  </style>
27
- <script type="module" crossorigin src="/assets/index-DoWxl4fH.js"></script>
28
- <link rel="stylesheet" crossorigin href="/assets/index-CX4XmW02.css">
27
+ <script type="module" crossorigin src="/assets/index-DxAcDLRC.js"></script>
28
+ <link rel="stylesheet" crossorigin href="/assets/index-Ds8Za-ns.css">
29
29
  </head>
30
30
  <body>
31
31
  <div id="root"></div>
@@ -0,0 +1,15 @@
1
+ {
2
+ "dashboard": {
3
+ "title": "Welcome",
4
+ "description": "Development environment for testing and integrating your applications."
5
+ },
6
+ "navbar": {
7
+ "dashboard": "Welcome",
8
+ "orders": "Orders",
9
+ "settings": "Settings",
10
+ "products_and_catalogs": "Products & Catalogs",
11
+ "help_and_support": "Help & Support",
12
+ "development_app": "Development App",
13
+ "plugin_test": "Plugin Test"
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "dashboard": {
3
+ "title": "Hoş Geldiniz",
4
+ "description": "Uygulamalarınızı test etmek ve entegre etmek için geliştirme ortamı."
5
+ },
6
+ "navbar": {
7
+ "dashboard": "Hoş Geldiniz",
8
+ "orders": "Siparişler",
9
+ "settings": "Ayarlar",
10
+ "products_and_catalogs": "Ürünler & Kataloglar",
11
+ "help_and_support": "Yardım & Destek",
12
+ "development_app": "Geliştirme Uygulaması",
13
+ "plugin_test": "Eklenti Testi"
14
+ }
15
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akinon/ui-shell-dev",
3
- "version": "1.5.3",
3
+ "version": "1.5.4",
4
4
  "private": false,
5
5
  "description": "Development shell application for Akinon UI Protocol plugins",
6
6
  "type": "module",
@@ -21,11 +21,14 @@
21
21
  "react-dom": "*",
22
22
  "react-router-dom": "^6.28.0",
23
23
  "vite": "*",
24
- "@akinon/app-shell": "1.4.1",
25
- "@akinon/icons": "1.1.0",
26
- "@akinon/fonts-jost-variable": "2.1.0",
27
- "@akinon/ui-utils": "1.1.0",
28
- "@akinon/ui-react": "1.3.1"
24
+ "@akinon/app-shell": "1.4.2",
25
+ "@akinon/fonts-jost-variable": "2.1.1",
26
+ "@akinon/icons": "1.1.1",
27
+ "@akinon/ui-react": "1.3.3",
28
+ "@akinon/ui-utils": "1.1.2",
29
+ "@akinon/ui-hooks": "1.1.1",
30
+ "@akinon/akidate": "1.1.2",
31
+ "@akinon/akilocale": "1.2.1"
29
32
  },
30
33
  "devDependencies": {
31
34
  "@types/cors": "^2.8.17",
@@ -35,7 +38,7 @@
35
38
  "@vitejs/plugin-react": "^5.0.0",
36
39
  "@vitejs/plugin-react-swc": "^3.7.2",
37
40
  "typescript": "*",
38
- "@akinon/typescript-config": "1.1.0"
41
+ "@akinon/typescript-config": "1.1.1"
39
42
  },
40
43
  "engines": {
41
44
  "node": ">=18"
@@ -0,0 +1,15 @@
1
+ {
2
+ "dashboard": {
3
+ "title": "Welcome",
4
+ "description": "Development environment for testing and integrating your applications."
5
+ },
6
+ "navbar": {
7
+ "dashboard": "Welcome",
8
+ "orders": "Orders",
9
+ "settings": "Settings",
10
+ "products_and_catalogs": "Products & Catalogs",
11
+ "help_and_support": "Help & Support",
12
+ "development_app": "Development App",
13
+ "plugin_test": "Plugin Test"
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "dashboard": {
3
+ "title": "Hoş Geldiniz",
4
+ "description": "Uygulamalarınızı test etmek ve entegre etmek için geliştirme ortamı."
5
+ },
6
+ "navbar": {
7
+ "dashboard": "Hoş Geldiniz",
8
+ "orders": "Siparişler",
9
+ "settings": "Ayarlar",
10
+ "products_and_catalogs": "Ürünler & Kataloglar",
11
+ "help_and_support": "Yardım & Destek",
12
+ "development_app": "Geliştirme Uygulaması",
13
+ "plugin_test": "Eklenti Testi"
14
+ }
15
+ }
@@ -1,5 +1,14 @@
1
+ import { akidate } from '@akinon/akidate';
2
+ import { AKILOCALE_STORAGE_KEY } from '@akinon/akilocale';
3
+ import { useTranslation } from '@akinon/akilocale/react';
4
+ import { useAppShell } from '@akinon/app-shell';
5
+ import { useLocalStorage } from '@akinon/ui-hooks';
1
6
  import { Button } from '@akinon/ui-react';
2
- import React from 'react';
7
+ import type { FC } from 'react';
8
+ import React, { useEffect } from 'react';
9
+
10
+ import { ApplicationLocales } from '../constants/locales';
11
+ import { LocaleChanger } from './LocaleChanger';
3
12
 
4
13
  // Get version from package.json via Vite environment variable
5
14
  const getVersion = (): string => {
@@ -11,10 +20,29 @@ interface HeaderProps {
11
20
  onToggleRightSider: () => void;
12
21
  }
13
22
 
14
- export const Header: React.FC<HeaderProps> = ({
23
+ export const Header: FC<HeaderProps> = ({
15
24
  onToggleLeftSider,
16
25
  onToggleRightSider
17
26
  }) => {
27
+ const { i18n } = useTranslation();
28
+ const { updateLocale } = useAppShell();
29
+ const [, setLocale] = useLocalStorage<string | null>(
30
+ AKILOCALE_STORAGE_KEY,
31
+ null,
32
+ { raw: true }
33
+ );
34
+
35
+ const handleLocaleChange = (locale: string) => {
36
+ i18n.changeLanguage(locale);
37
+ akidate.setLocale(locale);
38
+ setLocale(locale);
39
+ updateLocale(locale);
40
+ };
41
+
42
+ useEffect(() => {
43
+ updateLocale(i18n.language);
44
+ }, [updateLocale, i18n.language]);
45
+
18
46
  return (
19
47
  <>
20
48
  <div className="flex items-center">
@@ -50,6 +78,11 @@ export const Header: React.FC<HeaderProps> = ({
50
78
  </div>
51
79
 
52
80
  <div className="flex items-center">
81
+ <LocaleChanger
82
+ currentLocale={i18n.language}
83
+ locales={ApplicationLocales}
84
+ onLocaleChange={handleLocaleChange}
85
+ />
53
86
  <Button
54
87
  type="text"
55
88
  icon="siralama"
@@ -0,0 +1,42 @@
1
+ import { Button, Dropdown } from '@akinon/ui-react';
2
+ import type { FC } from 'react';
3
+ import React, { useMemo } from 'react';
4
+
5
+ import type { LocaleChangerProps } from '../types';
6
+
7
+ export const LocaleChanger: FC<LocaleChangerProps> = ({
8
+ currentLocale,
9
+ locales,
10
+ onLocaleChange
11
+ }) => {
12
+ const handleLocaleChange = (locale: string) => () => {
13
+ onLocaleChange(locale);
14
+ };
15
+
16
+ const items = useMemo(
17
+ () =>
18
+ Object.keys(locales).map((key: string) => ({
19
+ key: `locale-${key}`,
20
+ label: locales[key].name,
21
+ onClick: handleLocaleChange(key)
22
+ })),
23
+ [locales]
24
+ );
25
+
26
+ return (
27
+ <div className="header-locale">
28
+ <Dropdown
29
+ menu={{
30
+ items
31
+ }}
32
+ trigger={['click']}
33
+ placement="bottomRight"
34
+ arrow
35
+ >
36
+ <Button type="text" className="flex items-center">
37
+ {locales[currentLocale]?.name ?? currentLocale}
38
+ </Button>
39
+ </Dropdown>
40
+ </div>
41
+ );
42
+ };
@@ -1,3 +1,4 @@
1
+ import { useTranslation } from '@akinon/akilocale/react';
1
2
  import { useAppShell } from '@akinon/app-shell';
2
3
  import { IMenuProps, Menu, TMenuItemType } from '@akinon/ui-react';
3
4
  import { useEffect, useMemo, useState } from 'react';
@@ -13,17 +14,18 @@ import {
13
14
 
14
15
  export const Navigation: React.FC = () => {
15
16
  const { apps, configs } = useAppShell();
17
+ const { t } = useTranslation();
16
18
  const location = useLocation();
17
19
  const navigate = useNavigate();
18
20
  const [selectedKey, setSelectedKey] = useState('/');
19
21
  const [openKeys, setOpenKeys] = useState<string[]>([]);
20
- const items = getStaticNavigationItems();
21
- const itemsBottom = getStaticNavigationItemsBottom();
22
+ const items = getStaticNavigationItems(t);
23
+ const itemsBottom = getStaticNavigationItemsBottom(t);
22
24
  const fullpageItems = getFullpageNavigationItems(
23
25
  apps,
24
26
  configs as Map<number, unknown>
25
27
  );
26
- const pluginItems = getPluginNavigationItems(apps);
28
+ const pluginItems = getPluginNavigationItems(apps, t);
27
29
  const navigationItems = useMemo(
28
30
  () => [...items, ...fullpageItems, ...pluginItems, ...itemsBottom],
29
31
  [items, fullpageItems, pluginItems, itemsBottom]
@@ -0,0 +1,12 @@
1
+ export const ApplicationLocales = {
2
+ en: {
3
+ locale: 'en',
4
+ isoLocale: 'en-us',
5
+ name: 'English'
6
+ },
7
+ tr: {
8
+ locale: 'tr',
9
+ isoLocale: 'tr-tr',
10
+ name: 'Türkçe'
11
+ }
12
+ };
package/src/main.tsx CHANGED
@@ -2,12 +2,20 @@ import '@akinon/fonts-jost-variable';
2
2
  import '@akinon/ui-utils';
3
3
  import './global.scss';
4
4
 
5
+ import { akidate } from '@akinon/akidate';
6
+ import { AKILOCALE_STORAGE_KEY } from '@akinon/akilocale';
7
+ import { i18n } from '@akinon/akilocale/react';
5
8
  import React from 'react';
6
9
  import ReactDOM from 'react-dom/client';
7
10
 
8
11
  import { ShellApp } from './shell-app';
9
12
  import type { ShellConfig } from './types';
10
13
 
14
+ const initialLocale = localStorage.getItem(AKILOCALE_STORAGE_KEY) || 'en';
15
+
16
+ i18n.changeLanguage(initialLocale);
17
+ akidate.setLocale(initialLocale);
18
+
11
19
  // Get configuration injected by the server
12
20
  const config: ShellConfig = (
13
21
  window as unknown as { __SHELL_CONFIG__?: ShellConfig }
@@ -1,5 +1,6 @@
1
1
  import './pages.scss';
2
2
 
3
+ import { useTranslation } from '@akinon/akilocale/react';
3
4
  import React from 'react';
4
5
 
5
6
  import { PageContent } from '../components/PageContent';
@@ -7,12 +8,14 @@ import { PageHeading } from '../components/PageHeading';
7
8
  import { WelcomePage } from '../components/WelcomePage';
8
9
 
9
10
  export const PageDashboard: React.FC = () => {
11
+ const { t } = useTranslation();
12
+
10
13
  return (
11
14
  <div className="page-dashboard">
12
15
  <div className="page-heading-container">
13
16
  <PageHeading
14
- title="Welcome"
15
- description="Development environment for testing and integrating your applications"
17
+ title={t('dashboard.title')}
18
+ description={t('dashboard.description')}
16
19
  />
17
20
  </div>
18
21
  <div className="page-content-container">
package/src/providers.tsx CHANGED
@@ -1,3 +1,4 @@
1
+ import { useTranslation } from '@akinon/akilocale/react';
1
2
  import type { ApplicationActions, RegisteredApp } from '@akinon/app-shell';
2
3
  import { AppShellProvider, type ShellNavigation } from '@akinon/app-shell';
3
4
  import { notification } from '@akinon/ui-react';
@@ -15,6 +16,7 @@ export const Providers: React.FC<ProvidersProps> = ({ config, children }) => {
15
16
  const [api, contextHolder] = notification.useNotification();
16
17
  const [searchParams] = useSearchParams();
17
18
  const navigate = useNavigate();
19
+ const { i18n } = useTranslation();
18
20
 
19
21
  // Convert shell config to apps format for AppShellProvider
20
22
  const apps: RegisteredApp[] = config.apps.map((app, index) => ({
@@ -37,7 +39,7 @@ export const Providers: React.FC<ProvidersProps> = ({ config, children }) => {
37
39
  };
38
40
 
39
41
  const data = {
40
- lng: 'en',
42
+ lng: i18n.language,
41
43
  username: 'Developer',
42
44
  searchParams: Object.fromEntries([...searchParams]),
43
45
  ...(config.data || {})
@@ -86,7 +86,9 @@ export class ShellDevServer {
86
86
  configFile: path.resolve(shellRoot, 'vite.config.ts'),
87
87
  server: {
88
88
  middlewareMode: true,
89
- cors: true
89
+ cors: true,
90
+ port: this.config.shell.port,
91
+ strictPort: false
90
92
  },
91
93
  appType: 'spa'
92
94
  });
package/src/types.ts CHANGED
@@ -44,3 +44,15 @@ export interface ThemeConfig {
44
44
  }
45
45
 
46
46
  export type ViewType = 'welcome' | 'app' | 'plugin-test';
47
+
48
+ export interface LocaleOption {
49
+ locale: string;
50
+ isoLocale: string;
51
+ name: string;
52
+ }
53
+
54
+ export interface LocaleChangerProps {
55
+ currentLocale: string;
56
+ locales: Record<string, LocaleOption>;
57
+ onLocaleChange: (locale: string) => void;
58
+ }
@@ -1,6 +1,8 @@
1
1
  import type { RegisteredApp } from '@akinon/app-shell';
2
2
  import type { TMenuItemType } from '@akinon/ui-react';
3
3
 
4
+ type TranslateFunction = (key: string) => string;
5
+
4
6
  interface LocaleLabelMap {
5
7
  [locale: string]: string;
6
8
  }
@@ -28,55 +30,47 @@ function ensureLeadingSlash(path: string): string {
28
30
  return path.startsWith('/') ? path : `/${path}`;
29
31
  }
30
32
 
31
- const STATIC_NAVIGATION_ITEMS: TMenuItemType[] = [
32
- {
33
- key: '/',
34
- label: 'Welcome',
35
- icon: 'dashboard'
36
- },
37
- {
38
- key: '/products-and-catalogs',
39
- label: 'Products & Catalogs',
40
- icon: 'fiyatlistesi',
41
- disabled: true
42
- },
43
- {
44
- key: '/orders',
45
- label: 'Orders',
46
- icon: 'siparis',
47
- disabled: true
48
- }
49
- ];
50
-
51
- const STATIC_NAVIGATION_ITEMS_BOTTOM: TMenuItemType[] = [
52
- {
53
- key: '/placeholder-2',
54
- label: 'Help & Support',
55
- icon: 'bilgilendirme_servis',
56
- disabled: true
57
- },
58
- {
59
- key: '/placeholder-1',
60
- label: 'Settings',
61
- icon: 'ayarlar',
62
- disabled: true
63
- }
64
- ];
65
-
66
- const PLUGIN_NAVIGATION_ITEMS: TMenuItemType[] = [
67
- {
68
- key: '/plugin-test',
69
- label: 'Plugin Test',
70
- icon: 'box'
71
- }
72
- ];
73
-
74
- export const getStaticNavigationItems = () => {
75
- return STATIC_NAVIGATION_ITEMS;
33
+ export const getStaticNavigationItems = (
34
+ t: TranslateFunction
35
+ ): TMenuItemType[] => {
36
+ return [
37
+ {
38
+ key: '/',
39
+ label: t('navbar.dashboard'),
40
+ icon: 'dashboard'
41
+ },
42
+ {
43
+ key: '/products-and-catalogs',
44
+ label: t('navbar.products_and_catalogs'),
45
+ icon: 'fiyatlistesi',
46
+ disabled: true
47
+ },
48
+ {
49
+ key: '/orders',
50
+ label: t('navbar.orders'),
51
+ icon: 'siparis',
52
+ disabled: true
53
+ }
54
+ ];
76
55
  };
77
56
 
78
- export const getStaticNavigationItemsBottom = () => {
79
- return STATIC_NAVIGATION_ITEMS_BOTTOM;
57
+ export const getStaticNavigationItemsBottom = (
58
+ t: TranslateFunction
59
+ ): TMenuItemType[] => {
60
+ return [
61
+ {
62
+ key: '/placeholder-2',
63
+ label: t('navbar.help_and_support'),
64
+ icon: 'bilgilendirme_servis',
65
+ disabled: true
66
+ },
67
+ {
68
+ key: '/placeholder-1',
69
+ label: t('navbar.settings'),
70
+ icon: 'ayarlar',
71
+ disabled: true
72
+ }
73
+ ];
80
74
  };
81
75
 
82
76
  export const getFullpageNavigationItems = (
@@ -115,8 +109,19 @@ export const getFullpageNavigationItems = (
115
109
  });
116
110
  };
117
111
 
118
- export const getPluginNavigationItems = (apps: RegisteredApp[]) => {
112
+ export const getPluginNavigationItems = (
113
+ apps: RegisteredApp[],
114
+ t: TranslateFunction
115
+ ): TMenuItemType[] => {
119
116
  // Only show plugin test page if there are plugin type apps
120
117
  const hasPluginApps = apps.some(app => app.type === 'plugin');
121
- return hasPluginApps ? PLUGIN_NAVIGATION_ITEMS : [];
118
+ return hasPluginApps
119
+ ? [
120
+ {
121
+ key: '/plugin-test',
122
+ label: t('navbar.plugin_test'),
123
+ icon: 'box'
124
+ }
125
+ ]
126
+ : [];
122
127
  };