create-kuckit-app 0.2.1 → 0.3.1
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/package.json +1 -1
- package/templates/base/apps/web/package.json +13 -0
- package/templates/base/apps/web/src/components/KuckitModuleRoute.tsx +47 -10
- package/templates/base/apps/web/src/components/dashboard/app-sidebar.tsx +120 -0
- package/templates/base/apps/web/src/components/dashboard/dashboard-layout.tsx +46 -0
- package/templates/base/apps/web/src/components/dashboard/dashboard-overview.tsx +24 -0
- package/templates/base/apps/web/src/components/dashboard/index.ts +2 -0
- package/templates/base/apps/web/src/components/dashboard/nav-user.tsx +77 -0
- package/templates/base/apps/web/src/components/ui/avatar.tsx +39 -0
- package/templates/base/apps/web/src/components/ui/breadcrumb.tsx +102 -0
- package/templates/base/apps/web/src/components/ui/collapsible.tsx +21 -0
- package/templates/base/apps/web/src/components/ui/separator.tsx +26 -0
- package/templates/base/apps/web/src/components/ui/sheet.tsx +130 -0
- package/templates/base/apps/web/src/components/ui/sidebar.tsx +694 -0
- package/templates/base/apps/web/src/components/ui/skeleton.tsx +13 -0
- package/templates/base/apps/web/src/components/ui/tooltip.tsx +55 -0
- package/templates/base/apps/web/src/hooks/use-mobile.ts +19 -0
- package/templates/base/apps/web/src/index.css +133 -0
- package/templates/base/apps/web/src/lib/utils.ts +6 -0
- package/templates/base/apps/web/src/main.tsx +1 -0
- package/templates/base/apps/web/src/providers/KuckitProvider.tsx +1 -25
- package/templates/base/apps/web/src/routes/dashboard/$.tsx +9 -0
- package/templates/base/apps/web/src/routes/dashboard/index.tsx +6 -0
- package/templates/base/apps/web/src/routes/dashboard.tsx +25 -0
- package/templates/base/apps/web/vite.config.ts +5 -4
- package/templates/base/packages/items-module/package.json +4 -0
- package/templates/base/packages/items-module/src/ui/index.ts +1 -0
- package/templates/base/apps/web/src/lib/kuckit-router.ts +0 -42
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import * as TooltipPrimitive from '@radix-ui/react-tooltip'
|
|
3
|
+
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
|
|
6
|
+
function TooltipProvider({
|
|
7
|
+
delayDuration = 0,
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
|
10
|
+
return (
|
|
11
|
+
<TooltipPrimitive.Provider
|
|
12
|
+
data-slot="tooltip-provider"
|
|
13
|
+
delayDuration={delayDuration}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
|
20
|
+
return (
|
|
21
|
+
<TooltipProvider>
|
|
22
|
+
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
23
|
+
</TooltipProvider>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
|
28
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function TooltipContent({
|
|
32
|
+
className,
|
|
33
|
+
sideOffset = 0,
|
|
34
|
+
children,
|
|
35
|
+
...props
|
|
36
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
|
37
|
+
return (
|
|
38
|
+
<TooltipPrimitive.Portal>
|
|
39
|
+
<TooltipPrimitive.Content
|
|
40
|
+
data-slot="tooltip-content"
|
|
41
|
+
sideOffset={sideOffset}
|
|
42
|
+
className={cn(
|
|
43
|
+
'bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance',
|
|
44
|
+
className
|
|
45
|
+
)}
|
|
46
|
+
{...props}
|
|
47
|
+
>
|
|
48
|
+
{children}
|
|
49
|
+
<TooltipPrimitive.Arrow className="bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
|
50
|
+
</TooltipPrimitive.Content>
|
|
51
|
+
</TooltipPrimitive.Portal>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768
|
|
4
|
+
|
|
5
|
+
export function useIsMobile() {
|
|
6
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
|
|
7
|
+
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
|
10
|
+
const onChange = () => {
|
|
11
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
12
|
+
}
|
|
13
|
+
mql.addEventListener('change', onChange)
|
|
14
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
15
|
+
return () => mql.removeEventListener('change', onChange)
|
|
16
|
+
}, [])
|
|
17
|
+
|
|
18
|
+
return !!isMobile
|
|
19
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
4
|
+
|
|
5
|
+
@theme {
|
|
6
|
+
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif,
|
|
7
|
+
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
html,
|
|
11
|
+
body {
|
|
12
|
+
@apply bg-white dark:bg-gray-950;
|
|
13
|
+
|
|
14
|
+
@media (prefers-color-scheme: dark) {
|
|
15
|
+
color-scheme: dark;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
:root {
|
|
20
|
+
--radius: 0.625rem;
|
|
21
|
+
--background: oklch(1 0 0);
|
|
22
|
+
--foreground: oklch(0.145 0 0);
|
|
23
|
+
--card: oklch(1 0 0);
|
|
24
|
+
--card-foreground: oklch(0.145 0 0);
|
|
25
|
+
--popover: oklch(1 0 0);
|
|
26
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
27
|
+
--primary: oklch(0.205 0 0);
|
|
28
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
29
|
+
--secondary: oklch(0.97 0 0);
|
|
30
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
31
|
+
--muted: oklch(0.97 0 0);
|
|
32
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
33
|
+
--accent: oklch(0.97 0 0);
|
|
34
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
35
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
36
|
+
--border: oklch(0.922 0 0);
|
|
37
|
+
--input: oklch(0.922 0 0);
|
|
38
|
+
--ring: oklch(0.708 0 0);
|
|
39
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
40
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
41
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
42
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
43
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
44
|
+
--sidebar: oklch(0.985 0 0);
|
|
45
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
46
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
47
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
48
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
49
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
50
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
51
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.dark {
|
|
55
|
+
--background: oklch(0.145 0 0);
|
|
56
|
+
--foreground: oklch(0.985 0 0);
|
|
57
|
+
--card: oklch(0.205 0 0);
|
|
58
|
+
--card-foreground: oklch(0.985 0 0);
|
|
59
|
+
--popover: oklch(0.205 0 0);
|
|
60
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
61
|
+
--primary: oklch(0.922 0 0);
|
|
62
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
63
|
+
--secondary: oklch(0.269 0 0);
|
|
64
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
65
|
+
--muted: oklch(0.269 0 0);
|
|
66
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
67
|
+
--accent: oklch(0.269 0 0);
|
|
68
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
69
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
70
|
+
--border: oklch(1 0 0 / 10%);
|
|
71
|
+
--input: oklch(1 0 0 / 15%);
|
|
72
|
+
--ring: oklch(0.556 0 0);
|
|
73
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
74
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
75
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
76
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
77
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
78
|
+
--sidebar: oklch(0.205 0 0);
|
|
79
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
80
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
81
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
82
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
83
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
84
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
85
|
+
--sidebar-ring: oklch(0.556 0 0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@theme inline {
|
|
89
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
90
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
91
|
+
--radius-lg: var(--radius);
|
|
92
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
93
|
+
--color-background: var(--background);
|
|
94
|
+
--color-foreground: var(--foreground);
|
|
95
|
+
--color-card: var(--card);
|
|
96
|
+
--color-card-foreground: var(--card-foreground);
|
|
97
|
+
--color-popover: var(--popover);
|
|
98
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
99
|
+
--color-primary: var(--primary);
|
|
100
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
101
|
+
--color-secondary: var(--secondary);
|
|
102
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
103
|
+
--color-muted: var(--muted);
|
|
104
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
105
|
+
--color-accent: var(--accent);
|
|
106
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
107
|
+
--color-destructive: var(--destructive);
|
|
108
|
+
--color-border: var(--border);
|
|
109
|
+
--color-input: var(--input);
|
|
110
|
+
--color-ring: var(--ring);
|
|
111
|
+
--color-chart-1: var(--chart-1);
|
|
112
|
+
--color-chart-2: var(--chart-2);
|
|
113
|
+
--color-chart-3: var(--chart-3);
|
|
114
|
+
--color-chart-4: var(--chart-4);
|
|
115
|
+
--color-chart-5: var(--chart-5);
|
|
116
|
+
--color-sidebar: var(--sidebar);
|
|
117
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
118
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
119
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
120
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
121
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
122
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
123
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@layer base {
|
|
127
|
+
* {
|
|
128
|
+
@apply border-border outline-ring/50;
|
|
129
|
+
}
|
|
130
|
+
body {
|
|
131
|
+
@apply bg-background text-foreground;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client'
|
|
|
3
3
|
import { routeTree } from './routeTree.gen'
|
|
4
4
|
import { ServicesProvider } from './providers/ServicesProvider'
|
|
5
5
|
import { KuckitProvider } from './providers/KuckitProvider'
|
|
6
|
+
import './index.css'
|
|
6
7
|
|
|
7
8
|
declare module '@tanstack/react-router' {
|
|
8
9
|
interface Register {
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { createContext, useContext, useEffect, useState, useMemo, type ReactNode } from 'react'
|
|
2
|
-
import {
|
|
3
|
-
RouterProvider,
|
|
4
|
-
createRouter,
|
|
5
|
-
createRoute,
|
|
6
|
-
type RouteComponent,
|
|
7
|
-
} from '@tanstack/react-router'
|
|
2
|
+
import { RouterProvider, createRouter } from '@tanstack/react-router'
|
|
8
3
|
import {
|
|
9
4
|
loadKuckitClientModules,
|
|
10
5
|
KuckitNavProvider,
|
|
@@ -16,7 +11,6 @@ import {
|
|
|
16
11
|
type SlotRegistry,
|
|
17
12
|
} from '@kuckit/sdk-react'
|
|
18
13
|
import { routeTree } from '../routeTree.gen'
|
|
19
|
-
import { Route as rootRoute } from '../routes/__root'
|
|
20
14
|
import { useServices } from './ServicesProvider'
|
|
21
15
|
import { getClientModuleSpecs } from '../modules.client'
|
|
22
16
|
|
|
@@ -81,26 +75,8 @@ export function KuckitProvider({ children }: KuckitProviderProps) {
|
|
|
81
75
|
}
|
|
82
76
|
}, [orpc, queryClient])
|
|
83
77
|
|
|
84
|
-
// Build router with module routes
|
|
85
78
|
const router = useMemo(() => {
|
|
86
79
|
if (!loadResult) return null
|
|
87
|
-
|
|
88
|
-
const moduleRouteDefs = loadResult.routeRegistry.getAll()
|
|
89
|
-
const moduleRoutes = moduleRouteDefs.map((routeDef) =>
|
|
90
|
-
createRoute({
|
|
91
|
-
getParentRoute: () => rootRoute,
|
|
92
|
-
path: routeDef.path,
|
|
93
|
-
component: routeDef.component as RouteComponent,
|
|
94
|
-
})
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
if (moduleRoutes.length > 0) {
|
|
98
|
-
console.log(
|
|
99
|
-
`Loaded ${moduleRoutes.length} module routes:`,
|
|
100
|
-
moduleRouteDefs.map((r) => r.path)
|
|
101
|
-
)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
80
|
return createRouter({
|
|
105
81
|
routeTree,
|
|
106
82
|
defaultPreload: 'intent',
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { KuckitModuleRoute } from '@/components/KuckitModuleRoute'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Catch-all route for KuckitModule routes under /dashboard.
|
|
6
|
+
*/
|
|
7
|
+
export const Route = createFileRoute('/dashboard/$')({
|
|
8
|
+
component: KuckitModuleRoute,
|
|
9
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createFileRoute, Outlet, redirect } from '@tanstack/react-router'
|
|
2
|
+
import { DashboardLayout } from '@/components/dashboard/dashboard-layout'
|
|
3
|
+
import { createAuthClientService } from '../services/auth-client'
|
|
4
|
+
|
|
5
|
+
export const Route = createFileRoute('/dashboard')({
|
|
6
|
+
beforeLoad: async () => {
|
|
7
|
+
const authClient = createAuthClientService()
|
|
8
|
+
const session = await authClient.getSession()
|
|
9
|
+
if (!session.data) {
|
|
10
|
+
throw redirect({
|
|
11
|
+
to: '/login',
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
return { session }
|
|
15
|
+
},
|
|
16
|
+
component: DashboardLayoutRoute,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
function DashboardLayoutRoute() {
|
|
20
|
+
return (
|
|
21
|
+
<DashboardLayout>
|
|
22
|
+
<Outlet />
|
|
23
|
+
</DashboardLayout>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import react from '@vitejs/plugin-react'
|
|
1
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
3
2
|
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
|
|
4
|
-
import
|
|
3
|
+
import react from '@vitejs/plugin-react'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { defineConfig } from 'vite'
|
|
5
6
|
|
|
6
7
|
export default defineConfig({
|
|
7
|
-
plugins: [TanStackRouterVite(), react()],
|
|
8
|
+
plugins: [tailwindcss(), TanStackRouterVite(), react()],
|
|
8
9
|
resolve: {
|
|
9
10
|
alias: {
|
|
10
11
|
'@': path.resolve(__dirname, './src'),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ItemsPage } from './ItemsPage'
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { createRoute, type AnyRoute, type RouteComponent } from '@tanstack/react-router'
|
|
2
|
-
import type { QueryClient } from '@tanstack/react-query'
|
|
3
|
-
import type { RouteRegistry, RouteDefinition } from '@kuckit/sdk-react'
|
|
4
|
-
import type { ORPCUtils } from '../services/types'
|
|
5
|
-
|
|
6
|
-
export interface KuckitRouterContext {
|
|
7
|
-
orpc: ORPCUtils
|
|
8
|
-
queryClient: QueryClient
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Build TanStack Router routes from a RouteRegistry
|
|
13
|
-
*/
|
|
14
|
-
export function buildModuleRoutes(routeRegistry: RouteRegistry, rootRoute: AnyRoute): AnyRoute[] {
|
|
15
|
-
const routes = routeRegistry.getAll()
|
|
16
|
-
const routeMap = new Map<string, AnyRoute>()
|
|
17
|
-
|
|
18
|
-
for (const routeDef of routes) {
|
|
19
|
-
const route = createModuleRoute(routeDef, rootRoute)
|
|
20
|
-
routeMap.set(routeDef.id, route)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const topLevelRoutes = routes
|
|
24
|
-
.filter((r) => !r.parentRouteId || r.parentRouteId === '__root__')
|
|
25
|
-
.map((r) => routeMap.get(r.id)!)
|
|
26
|
-
.filter(Boolean)
|
|
27
|
-
|
|
28
|
-
return topLevelRoutes
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function createModuleRoute(routeDef: RouteDefinition, parentRoute: AnyRoute): AnyRoute {
|
|
32
|
-
return createRoute({
|
|
33
|
-
getParentRoute: () => parentRoute,
|
|
34
|
-
path: routeDef.path,
|
|
35
|
-
component: routeDef.component as RouteComponent,
|
|
36
|
-
...(routeDef.meta?.title && {
|
|
37
|
-
head: () => ({
|
|
38
|
-
meta: [{ title: routeDef.meta!.title }],
|
|
39
|
-
}),
|
|
40
|
-
}),
|
|
41
|
-
})
|
|
42
|
-
}
|