Skip to content

Commit 6b5bdcf

Browse files
committed
feat(AppHeader): enhance breadcrumb navigation and add mobile search functionality
feat(AppSidebar): localize application labels and improve user interface elements feat(LoadingScreen): update loading screen with logo and localized messages feat(ObjectView): localize object not found messages and button labels feat(ModeToggle): localize theme toggle options and improve button accessibility
1 parent 3d3380e commit 6b5bdcf

File tree

9 files changed

+199
-96
lines changed

9 files changed

+199
-96
lines changed

apps/console/src/App.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ function RecordDetailView({ dataSource, objects, onEdit }: any) {
3131
return (
3232
<div className="flex h-full items-center justify-center p-4">
3333
<Empty>
34-
<EmptyTitle>Object Not Found</EmptyTitle>
35-
<p>Object "{objectName}" definition missing.</p>
34+
<EmptyTitle>对象未找到</EmptyTitle>
35+
<p>对象 "{objectName}" 定义缺失。</p>
3636
</Empty>
3737
</div>
3838
);
@@ -66,7 +66,7 @@ function RecordDetailView({ dataSource, objects, onEdit }: any) {
6666
variant="outline"
6767
size="icon"
6868
onClick={() => setShowDebug(!showDebug)}
69-
title="Toggle Metadata Inspector"
69+
title="切换元数据检查器"
7070
className="bg-background/80 backdrop-blur shadow-sm"
7171
>
7272
<Code2 className="h-4 w-4" />
@@ -84,12 +84,12 @@ function RecordDetailView({ dataSource, objects, onEdit }: any) {
8484
{showDebug && (
8585
<div className="w-[400px] border-l bg-muted/30 p-0 overflow-hidden flex flex-col shrink-0 shadow-xl z-20 transition-all">
8686
<div className="p-3 border-b bg-muted/50 font-semibold text-sm flex items-center justify-between">
87-
<span>Metadata Inspector</span>
88-
<span className="text-xs text-muted-foreground">JSON Protocol</span>
87+
<span>元数据检查器</span>
88+
<span className="text-xs text-muted-foreground">JSON 协议</span>
8989
</div>
9090
<div className="flex-1 overflow-auto p-4 space-y-6">
9191
<div>
92-
<h4 className="text-xs font-bold uppercase text-muted-foreground mb-2">View Schema</h4>
92+
<h4 className="text-xs font-bold uppercase text-muted-foreground mb-2">视图配置</h4>
9393
<div className="relative rounded-md border bg-slate-950 text-slate-50 overflow-hidden">
9494
<pre className="text-xs p-3 overflow-auto max-h-[800px]">
9595
{JSON.stringify(detailSchema, null, 2)}
@@ -203,7 +203,7 @@ export function AppContent() {
203203
if (!activeApp) return (
204204
<div className="h-screen flex items-center justify-center">
205205
<Empty>
206-
<EmptyTitle>No Apps configured</EmptyTitle>
206+
<EmptyTitle>未配置应用</EmptyTitle>
207207
</Empty>
208208
</div>
209209
);
@@ -261,9 +261,9 @@ export function AppContent() {
261261
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
262262
<DialogContent className="sm:max-w-xl max-h-[90vh] flex flex-col gap-0 p-0 overflow-hidden">
263263
<DialogHeader className="p-6 pb-4 border-b">
264-
<DialogTitle>{editingRecord ? 'Edit' : 'Create'} {currentObjectDef?.label}</DialogTitle>
264+
<DialogTitle>{editingRecord ? '编辑' : '新建'} {currentObjectDef?.label}</DialogTitle>
265265
<DialogDescription>
266-
{editingRecord ? `Update details for ${currentObjectDef?.label}` : `Add a new ${currentObjectDef?.label} to your database.`}
266+
{editingRecord ? `更新 ${currentObjectDef?.label} 的详细信息` : `添加一条新的 ${currentObjectDef?.label} 记录`}
267267
</DialogDescription>
268268
</DialogHeader>
269269
<div className="flex-1 overflow-y-auto p-6">
@@ -286,8 +286,8 @@ export function AppContent() {
286286
onCancel: () => setIsDialogOpen(false),
287287
showSubmit: true,
288288
showCancel: true,
289-
submitText: 'Save Record',
290-
cancelText: 'Cancel'
289+
submitText: '保存记录',
290+
cancelText: '取消'
291291
}}
292292
dataSource={dataSource}
293293
/>

apps/console/src/components/AppHeader.tsx

Lines changed: 101 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,125 @@
1-
import { useLocation } from 'react-router-dom';
1+
import { useLocation, useParams, Link } from 'react-router-dom';
22
import {
33
Breadcrumb,
44
BreadcrumbItem,
55
BreadcrumbLink,
66
BreadcrumbList,
77
BreadcrumbPage,
88
BreadcrumbSeparator,
9+
SidebarTrigger,
10+
Input,
11+
Button,
12+
Separator,
913
} from '@object-ui/components';
14+
import { Search, Bell, HelpCircle } from 'lucide-react';
1015

1116
import { ModeToggle } from './mode-toggle';
1217

1318
export function AppHeader({ appName, objects }: { appName: string, objects: any[] }) {
1419
const location = useLocation();
15-
20+
const params = useParams();
21+
22+
// Parse the current route to build breadcrumbs
23+
const pathParts = location.pathname.split('/').filter(Boolean);
24+
// pathParts: ['apps', 'crm_app', 'contact', 'view', 'all'] or ['apps', 'crm_app', 'dashboard', 'crm_dashboard']
25+
26+
const appNameFromRoute = params.appName || pathParts[1];
27+
const routeType = pathParts[2]; // 'contact', 'dashboard', 'page', 'report'
28+
29+
// Determine breadcrumb items
30+
const breadcrumbItems: { label: string; href?: string }[] = [
31+
{ label: appName, href: `/apps/${appNameFromRoute}` }
32+
];
1633

17-
// Find current object if we are on an object route
18-
// Note: This logic assumes simple paths for now.
19-
const isObjectPage = location.pathname.startsWith('/') && location.pathname.length > 1;
20-
const currentObject = isObjectPage ? objects.find((o: any) => o.name === location.pathname.substring(1)) : null;
34+
if (routeType === 'dashboard') {
35+
breadcrumbItems.push({ label: '仪表盘' });
36+
if (pathParts[3]) {
37+
breadcrumbItems.push({ label: pathParts[3] });
38+
}
39+
} else if (routeType === 'page') {
40+
breadcrumbItems.push({ label: '页面' });
41+
if (pathParts[3]) {
42+
breadcrumbItems.push({ label: pathParts[3] });
43+
}
44+
} else if (routeType === 'report') {
45+
breadcrumbItems.push({ label: '报表' });
46+
if (pathParts[3]) {
47+
breadcrumbItems.push({ label: pathParts[3] });
48+
}
49+
} else if (routeType) {
50+
// Object route
51+
const currentObject = objects.find((o: any) => o.name === routeType);
52+
if (currentObject) {
53+
breadcrumbItems.push({
54+
label: currentObject.label || routeType,
55+
href: `/apps/${appNameFromRoute}/${routeType}`
56+
});
57+
58+
// Check if viewing a specific record
59+
if (pathParts[3] === 'record' && pathParts[4]) {
60+
breadcrumbItems.push({ label: `记录 ${pathParts[4].slice(0, 8)}...` });
61+
} else if (pathParts[3] === 'view' && pathParts[4]) {
62+
breadcrumbItems.push({ label: pathParts[4] });
63+
}
64+
}
65+
}
2166

2267
return (
23-
<div className="flex items-center justify-between w-full h-full px-4">
68+
<div className="flex items-center justify-between w-full h-full px-2 md:px-4 gap-2">
2469
<div className="flex items-center gap-2">
25-
<Breadcrumb>
70+
{/* Mobile sidebar trigger */}
71+
<SidebarTrigger className="md:hidden" />
72+
<Separator orientation="vertical" className="h-4 md:hidden" />
73+
74+
<Breadcrumb className="hidden sm:flex">
2675
<BreadcrumbList>
27-
<BreadcrumbItem className="hidden md:block">
28-
<BreadcrumbLink href="#">
29-
{appName}
30-
</BreadcrumbLink>
31-
</BreadcrumbItem>
32-
{currentObject && (
33-
<>
34-
<BreadcrumbSeparator className="hidden md:block" />
35-
<BreadcrumbItem>
36-
<BreadcrumbPage>{currentObject.label}</BreadcrumbPage>
37-
</BreadcrumbItem>
38-
</>
39-
)}
76+
{breadcrumbItems.map((item, index) => (
77+
<BreadcrumbItem key={index}>
78+
{index > 0 && <BreadcrumbSeparator />}
79+
{index === breadcrumbItems.length - 1 || !item.href ? (
80+
<BreadcrumbPage>{item.label}</BreadcrumbPage>
81+
) : (
82+
<BreadcrumbLink asChild>
83+
<Link to={item.href}>{item.label}</Link>
84+
</BreadcrumbLink>
85+
)}
86+
</BreadcrumbItem>
87+
))}
4088
</BreadcrumbList>
4189
</Breadcrumb>
90+
91+
{/* Mobile: Just show current page */}
92+
<span className="text-sm font-medium sm:hidden truncate max-w-[150px]">
93+
{breadcrumbItems[breadcrumbItems.length - 1]?.label || appName}
94+
</span>
4295
</div>
43-
<div>
96+
97+
<div className="flex items-center gap-1 md:gap-2">
98+
{/* Search - Desktop */}
99+
<div className="hidden lg:flex relative">
100+
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
101+
<Input
102+
placeholder="搜索..."
103+
className="w-[200px] xl:w-[280px] pl-8 h-8 bg-muted/50"
104+
/>
105+
</div>
106+
107+
{/* Search button - Mobile/Tablet */}
108+
<Button variant="ghost" size="icon" className="lg:hidden h-8 w-8">
109+
<Search className="h-4 w-4" />
110+
</Button>
111+
112+
{/* Notifications */}
113+
<Button variant="ghost" size="icon" className="h-8 w-8 hidden sm:flex">
114+
<Bell className="h-4 w-4" />
115+
</Button>
116+
117+
{/* Help */}
118+
<Button variant="ghost" size="icon" className="h-8 w-8 hidden md:flex">
119+
<HelpCircle className="h-4 w-4" />
120+
</Button>
121+
122+
{/* Theme toggle */}
44123
<ModeToggle />
45124
</div>
46125
</div>

apps/console/src/components/AppSidebar.tsx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export function AppSidebar({ activeAppName, onAppChange }: { activeAppName: stri
9797
<div className="grid flex-1 text-left text-sm leading-tight">
9898
<span className="truncate font-semibold">{activeApp.label}</span>
9999
<span className="truncate text-xs">
100-
{activeApp.description || `${activeApps.length} Apps Available`}
100+
{activeApp.description || `${activeApps.length} 个应用`}
101101
</span>
102102
</div>
103103
<ChevronsUpDown className="ml-auto" />
@@ -110,7 +110,7 @@ export function AppSidebar({ activeAppName, onAppChange }: { activeAppName: stri
110110
sideOffset={4}
111111
>
112112
<DropdownMenuLabel className="text-xs text-muted-foreground">
113-
Switch Application
113+
切换应用
114114
</DropdownMenuLabel>
115115
{activeApps.map((app: any) => (
116116
<DropdownMenuItem
@@ -122,15 +122,15 @@ export function AppSidebar({ activeAppName, onAppChange }: { activeAppName: stri
122122
{app.icon ? React.createElement(getIcon(app.icon), { className: "size-3" }) : <Database className="size-3" />}
123123
</div>
124124
{app.label}
125-
{/* {activeApp.name === app.name && <Check className="ml-auto h-4 w-4" />} */}
125+
{activeApp.name === app.name && <span className="ml-auto text-xs"></span>}
126126
</DropdownMenuItem>
127127
))}
128128
<DropdownMenuSeparator />
129129
<DropdownMenuItem className="gap-2 p-2">
130130
<div className="flex size-6 items-center justify-center rounded-md border bg-background">
131131
<Plus className="size-4" />
132132
</div>
133-
<div className="font-medium text-muted-foreground">Add App</div>
133+
<div className="font-medium text-muted-foreground">添加应用</div>
134134
</DropdownMenuItem>
135135
</DropdownMenuContent>
136136
</DropdownMenu>
@@ -152,12 +152,12 @@ export function AppSidebar({ activeAppName, onAppChange }: { activeAppName: stri
152152
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
153153
>
154154
<Avatar className="h-8 w-8 rounded-lg">
155-
<AvatarImage src="/avatars/user.jpg" alt="User" />
156-
<AvatarFallback className="rounded-lg">ME</AvatarFallback>
155+
<AvatarImage src="/avatars/user.jpg" alt="用户" />
156+
<AvatarFallback className="rounded-lg bg-primary text-primary-foreground"></AvatarFallback>
157157
</Avatar>
158158
<div className="grid flex-1 text-left text-sm leading-tight">
159-
<span className="truncate font-semibold">John Doe</span>
160-
<span className="truncate text-xs">admin@example.com</span>
159+
<span className="truncate font-semibold">管理员</span>
160+
<span className="truncate text-xs text-muted-foreground">admin@example.com</span>
161161
</div>
162162
<ChevronsUpDown className="ml-auto size-4" />
163163
</SidebarMenuButton>
@@ -171,26 +171,26 @@ export function AppSidebar({ activeAppName, onAppChange }: { activeAppName: stri
171171
<DropdownMenuLabel className="p-0 font-normal">
172172
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
173173
<Avatar className="h-8 w-8 rounded-lg">
174-
<AvatarImage src="/avatars/user.jpg" alt="User" />
175-
<AvatarFallback className="rounded-lg">ME</AvatarFallback>
174+
<AvatarImage src="/avatars/user.jpg" alt="用户" />
175+
<AvatarFallback className="rounded-lg bg-primary text-primary-foreground"></AvatarFallback>
176176
</Avatar>
177177
<div className="grid flex-1 text-left text-sm leading-tight">
178-
<span className="truncate font-semibold">John Doe</span>
179-
<span className="truncate text-xs">admin@example.com</span>
178+
<span className="truncate font-semibold">管理员</span>
179+
<span className="truncate text-xs text-muted-foreground">admin@example.com</span>
180180
</div>
181181
</div>
182182
</DropdownMenuLabel>
183183
<DropdownMenuSeparator />
184184
<DropdownMenuGroup>
185185
<DropdownMenuItem>
186186
<Settings className="mr-2 h-4 w-4" />
187-
Settings
187+
设置
188188
</DropdownMenuItem>
189189
</DropdownMenuGroup>
190190
<DropdownMenuSeparator />
191-
<DropdownMenuItem>
191+
<DropdownMenuItem className="text-destructive focus:text-destructive">
192192
<LogOut className="mr-2 h-4 w-4" />
193-
Log out
193+
退出登录
194194
</DropdownMenuItem>
195195
</DropdownMenuContent>
196196
</DropdownMenu>

apps/console/src/components/DashboardView.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ export function DashboardView() {
2222
return (
2323
<div className="h-full flex items-center justify-center p-8">
2424
<Empty>
25-
<EmptyTitle>Dashboard Not Found</EmptyTitle>
26-
<EmptyDescription>The dashboard "{dashboardName}" could not be found.</EmptyDescription>
25+
<EmptyTitle>仪表盘未找到</EmptyTitle>
26+
<EmptyDescription>仪表盘 "{dashboardName}" 不存在。</EmptyDescription>
2727
</Empty>
2828
</div>
2929
);
@@ -45,7 +45,7 @@ export function DashboardView() {
4545
className="gap-2"
4646
>
4747
<Code2 className="h-4 w-4" />
48-
Metadata
48+
元数据
4949
</Button>
5050
</div>
5151

@@ -57,12 +57,12 @@ export function DashboardView() {
5757
{showDebug && (
5858
<div className="w-[400px] border-l bg-muted/30 p-0 overflow-hidden flex flex-col shrink-0 shadow-xl z-20 transition-all">
5959
<div className="p-3 border-b bg-muted/50 font-semibold text-sm flex items-center justify-between">
60-
<span>Metadata Inspector</span>
61-
<span className="text-xs text-muted-foreground">JSON Protocol</span>
60+
<span>元数据检查器</span>
61+
<span className="text-xs text-muted-foreground">JSON 协议</span>
6262
</div>
6363
<div className="flex-1 overflow-auto p-4 space-y-6">
6464
<div>
65-
<h4 className="text-xs font-bold uppercase text-muted-foreground mb-2">Dashboard Configuration</h4>
65+
<h4 className="text-xs font-bold uppercase text-muted-foreground mb-2">仪表盘配置</h4>
6666
<div className="relative rounded-md border bg-slate-950 text-slate-50 overflow-hidden">
6767
<pre className="text-xs p-3 overflow-auto max-h-[800px]">
6868
{JSON.stringify(dashboard, null, 2)}

apps/console/src/components/LoadingScreen.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,30 @@
11

22
import { Spinner } from '@object-ui/components';
3+
import { Database } from 'lucide-react';
34

45
export function LoadingScreen() {
56
return (
67
<div className="flex flex-col items-center justify-center h-screen bg-background">
7-
<div className="flex items-center gap-2">
8-
<Spinner className="h-4 w-4" />
9-
<span className="text-sm text-muted-foreground">Initializing ObjectStack Console...</span>
8+
<div className="flex flex-col items-center gap-6">
9+
{/* Logo/Icon */}
10+
<div className="relative">
11+
<div className="absolute inset-0 bg-primary/20 rounded-2xl blur-xl animate-pulse" />
12+
<div className="relative bg-gradient-to-br from-primary to-primary/80 p-4 rounded-2xl shadow-lg">
13+
<Database className="h-10 w-10 text-primary-foreground" />
14+
</div>
15+
</div>
16+
17+
{/* Title */}
18+
<div className="text-center space-y-2">
19+
<h1 className="text-2xl font-bold tracking-tight">ObjectStack Console</h1>
20+
<p className="text-sm text-muted-foreground">正在初始化应用程序...</p>
21+
</div>
22+
23+
{/* Loading indicator */}
24+
<div className="flex items-center gap-3 px-4 py-2 bg-muted/50 rounded-full">
25+
<Spinner className="h-4 w-4 text-primary" />
26+
<span className="text-sm text-muted-foreground">连接数据源</span>
27+
</div>
1028
</div>
1129
</div>
1230
);

0 commit comments

Comments
 (0)