create-better-t-stack 3.12.5 → 3.12.6
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/cli.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-D3EHRs6z.mjs → src-Ci2sRCrb.mjs} +31 -20
- package/package.json +2 -2
- package/templates/auth/better-auth/convex/native/uniwind/components/sign-in.tsx.hbs +32 -50
- package/templates/auth/better-auth/convex/native/uniwind/components/sign-up.tsx.hbs +40 -57
- package/templates/auth/better-auth/native/uniwind/components/sign-in.tsx.hbs +32 -35
- package/templates/auth/better-auth/native/uniwind/components/sign-up.tsx.hbs +49 -37
- package/templates/auth/better-auth/web/nuxt/app/components/SignInForm.vue.hbs +40 -35
- package/templates/auth/better-auth/web/nuxt/app/components/SignUpForm.vue.hbs +47 -40
- package/templates/auth/better-auth/web/nuxt/app/middleware/auth.ts.hbs +9 -7
- package/templates/auth/better-auth/web/nuxt/app/pages/dashboard.vue.hbs +61 -29
- package/templates/auth/better-auth/web/nuxt/app/pages/login.vue.hbs +6 -3
- package/templates/backend/server/elysia/src/index.ts.hbs +8 -3
- package/templates/backend/server/express/src/index.ts.hbs +8 -3
- package/templates/backend/server/fastify/src/index.ts.hbs +8 -3
- package/templates/backend/server/hono/src/index.ts.hbs +16 -6
- package/templates/examples/ai/fullstack/next/src/app/api/ai/route.ts.hbs +8 -3
- package/templates/examples/ai/fullstack/tanstack-start/src/routes/api/ai/$.ts.hbs +8 -3
- package/templates/examples/ai/native/bare/polyfills.js +14 -11
- package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +104 -124
- package/templates/examples/ai/web/nuxt/app/pages/ai.vue.hbs +22 -22
- package/templates/examples/todo/native/uniwind/app/(drawer)/todos.tsx.hbs +67 -80
- package/templates/examples/todo/web/nuxt/app/pages/todos.vue.hbs +115 -90
- package/templates/frontend/native/uniwind/app/(drawer)/index.tsx.hbs +60 -54
- package/templates/frontend/native/uniwind/app/+not-found.tsx.hbs +16 -21
- package/templates/frontend/native/uniwind/app/modal.tsx.hbs +18 -34
- package/templates/frontend/native/uniwind/package.json.hbs +10 -10
- package/templates/frontend/nuxt/app/components/Header.vue.hbs +25 -29
- package/templates/frontend/nuxt/app/layouts/default.vue.hbs +7 -8
- package/templates/frontend/nuxt/app/pages/index.vue.hbs +58 -39
- package/templates/frontend/nuxt/package.json.hbs +1 -1
- package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx.hbs +5 -5
- package/templates/frontend/nuxt/app/components/Loader.vue.hbs +0 -5
- package/templates/frontend/nuxt/app/components/ModeToggle.vue.hbs +0 -25
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Text, View
|
|
1
|
+
import { Text, View } from "react-native";
|
|
2
2
|
import { Container } from "@/components/container";
|
|
3
3
|
{{#if (eq api "orpc")}}
|
|
4
4
|
import { useQuery } from "@tanstack/react-query";
|
|
@@ -27,7 +27,7 @@ import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
|
27
27
|
{{#unless (or (eq backend "none") (and (eq backend "convex") (eq auth "better-auth")))}}
|
|
28
28
|
import { Ionicons } from "@expo/vector-icons";
|
|
29
29
|
{{/unless}}
|
|
30
|
-
import {
|
|
30
|
+
import { Button, Chip, Divider, Spinner, Surface, useThemeColor } from "heroui-native";
|
|
31
31
|
|
|
32
32
|
export default function Home() {
|
|
33
33
|
{{#if (eq api "orpc")}}
|
|
@@ -48,7 +48,6 @@ const user = useQuery(api.auth.getCurrentUser, isAuthenticated ? {} : "skip");
|
|
|
48
48
|
const healthCheck = useQuery(api.healthCheck.get);
|
|
49
49
|
{{/if}}
|
|
50
50
|
{{#unless (eq backend "none")}}
|
|
51
|
-
const mutedColor = useThemeColor("muted");
|
|
52
51
|
const successColor = useThemeColor("success");
|
|
53
52
|
const dangerColor = useThemeColor("danger");
|
|
54
53
|
|
|
@@ -64,28 +63,32 @@ const isLoading = healthCheck?.isLoading;
|
|
|
64
63
|
{{/unless}}
|
|
65
64
|
|
|
66
65
|
return (
|
|
67
|
-
<Container className="p-
|
|
68
|
-
<View className="py-
|
|
69
|
-
<Text className="text-
|
|
70
|
-
|
|
66
|
+
<Container className="p-4">
|
|
67
|
+
<View className="py-6 mb-4">
|
|
68
|
+
<Text className="text-3xl font-semibold text-foreground tracking-tight">
|
|
69
|
+
Better T Stack
|
|
71
70
|
</Text>
|
|
71
|
+
<Text className="text-muted text-sm mt-1">Full-stack TypeScript starter</Text>
|
|
72
72
|
</View>
|
|
73
73
|
|
|
74
74
|
{{#unless (or (eq backend "none") (and (eq backend "convex") (eq auth "better-auth")))}}
|
|
75
|
-
<
|
|
76
|
-
<View className="flex-row items-center justify-between mb-
|
|
77
|
-
<
|
|
75
|
+
<Surface variant="secondary" className="p-4 rounded-lg">
|
|
76
|
+
<View className="flex-row items-center justify-between mb-3">
|
|
77
|
+
<Text className="text-foreground font-medium">System Status</Text>
|
|
78
78
|
<Chip variant="secondary" color={isConnected ? "success" : "danger" } size="sm">
|
|
79
79
|
<Chip.Label>
|
|
80
80
|
{isConnected ? "LIVE" : "OFFLINE"}
|
|
81
81
|
</Chip.Label>
|
|
82
82
|
</Chip>
|
|
83
83
|
</View>
|
|
84
|
-
|
|
84
|
+
|
|
85
|
+
<Divider className="mb-3" />
|
|
86
|
+
|
|
87
|
+
<Surface variant="tertiary" className="p-3 rounded-md">
|
|
85
88
|
<View className="flex-row items-center">
|
|
86
|
-
<View className={`w-
|
|
89
|
+
<View className={`w-2 h-2 rounded-full mr-3 ${ isConnected ? "bg-success" : "bg-muted" }`} />
|
|
87
90
|
<View className="flex-1">
|
|
88
|
-
<Text className="text-foreground font-medium
|
|
91
|
+
<Text className="text-foreground text-sm font-medium">
|
|
89
92
|
{{#if (eq backend "convex")}}
|
|
90
93
|
Convex Backend
|
|
91
94
|
{{else}}
|
|
@@ -94,77 +97,80 @@ return (
|
|
|
94
97
|
{{/unless}}
|
|
95
98
|
{{/if}}
|
|
96
99
|
</Text>
|
|
97
|
-
<
|
|
100
|
+
<Text className="text-muted text-xs mt-0.5">
|
|
98
101
|
{isLoading
|
|
99
102
|
? "Checking connection..."
|
|
100
103
|
: isConnected
|
|
101
104
|
? "Connected to API"
|
|
102
105
|
: "API Disconnected"}
|
|
103
|
-
</
|
|
106
|
+
</Text>
|
|
104
107
|
</View>
|
|
105
|
-
{isLoading &&
|
|
106
|
-
<Ionicons name="hourglass-outline" size={20} color={mutedColor} />
|
|
107
|
-
)}
|
|
108
|
+
{isLoading && <Spinner size="sm" />}
|
|
108
109
|
{!isLoading && isConnected && (
|
|
109
|
-
<Ionicons name="checkmark-circle" size={
|
|
110
|
+
<Ionicons name="checkmark-circle" size={18} color={successColor} />
|
|
110
111
|
)}
|
|
111
112
|
{!isLoading && !isConnected && (
|
|
112
|
-
<Ionicons name="close-circle" size={
|
|
113
|
+
<Ionicons name="close-circle" size={18} color={dangerColor} />
|
|
113
114
|
)}
|
|
114
115
|
</View>
|
|
115
|
-
</
|
|
116
|
-
</
|
|
116
|
+
</Surface>
|
|
117
|
+
</Surface>
|
|
117
118
|
{{/unless}}
|
|
118
119
|
|
|
119
120
|
{{#if (and (eq backend "convex") (eq auth "clerk"))}}
|
|
120
121
|
<Authenticated>
|
|
121
|
-
<
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
</
|
|
122
|
+
<Surface variant="secondary" className="mt-4 p-4 rounded-lg">
|
|
123
|
+
<View className="flex-row items-center justify-between">
|
|
124
|
+
<View className="flex-1">
|
|
125
|
+
<Text className="text-foreground font-medium">{user?.emailAddresses[0].emailAddress}</Text>
|
|
126
|
+
<Text className="text-muted text-xs mt-0.5">Private: {privateData?.message}</Text>
|
|
127
|
+
</View>
|
|
128
|
+
<SignOutButton />
|
|
129
|
+
</View>
|
|
130
|
+
</Surface>
|
|
130
131
|
</Authenticated>
|
|
131
132
|
<Unauthenticated>
|
|
132
|
-
<View className="mt-
|
|
133
|
+
<View className="mt-4 gap-3">
|
|
133
134
|
<Link href="/(auth)/sign-in" asChild>
|
|
134
|
-
|
|
135
|
+
<Button variant="secondary"><Button.Label>Sign In</Button.Label></Button>
|
|
135
136
|
</Link>
|
|
136
137
|
<Link href="/(auth)/sign-up" asChild>
|
|
137
|
-
|
|
138
|
+
<Button variant="ghost"><Button.Label>Sign Up</Button.Label></Button>
|
|
138
139
|
</Link>
|
|
139
140
|
</View>
|
|
140
141
|
</Unauthenticated>
|
|
141
142
|
<AuthLoading>
|
|
142
|
-
<
|
|
143
|
+
<View className="mt-4 items-center">
|
|
144
|
+
<Spinner size="sm" />
|
|
145
|
+
</View>
|
|
143
146
|
</AuthLoading>
|
|
144
147
|
{{/if}}
|
|
145
148
|
|
|
146
149
|
{{#if (and (eq backend "convex") (eq auth "better-auth"))}}
|
|
147
150
|
{user ? (
|
|
148
|
-
<
|
|
149
|
-
<View className="
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
151
|
+
<Surface variant="secondary" className="mb-4 p-4 rounded-lg">
|
|
152
|
+
<View className="flex-row items-center justify-between">
|
|
153
|
+
<View className="flex-1">
|
|
154
|
+
<Text className="text-foreground font-medium">{user.name}</Text>
|
|
155
|
+
<Text className="text-muted text-xs mt-0.5">{user.email}</Text>
|
|
156
|
+
</View>
|
|
157
|
+
<Button
|
|
158
|
+
variant="destructive"
|
|
159
|
+
size="sm"
|
|
160
|
+
onPress={() => {
|
|
161
|
+
authClient.signOut();
|
|
162
|
+
}}
|
|
158
163
|
>
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
164
|
+
Sign Out
|
|
165
|
+
</Button>
|
|
166
|
+
</View>
|
|
167
|
+
</Surface>
|
|
162
168
|
) : null}
|
|
163
|
-
<
|
|
169
|
+
<Surface variant="secondary" className="p-4 rounded-lg">
|
|
164
170
|
<Text className="text-foreground font-medium mb-2">API Status</Text>
|
|
165
171
|
<View className="flex-row items-center gap-2">
|
|
166
|
-
<View className={`w-
|
|
167
|
-
<Text className="text-muted">
|
|
172
|
+
<View className={`w-2 h-2 rounded-full ${healthCheck==="OK" ? "bg-success" : "bg-danger" }`} />
|
|
173
|
+
<Text className="text-muted text-xs">
|
|
168
174
|
{healthCheck === undefined
|
|
169
175
|
? "Checking..."
|
|
170
176
|
: healthCheck === "OK"
|
|
@@ -172,12 +178,12 @@ return (
|
|
|
172
178
|
: "API Disconnected"}
|
|
173
179
|
</Text>
|
|
174
180
|
</View>
|
|
175
|
-
</
|
|
181
|
+
</Surface>
|
|
176
182
|
{!user && (
|
|
177
|
-
|
|
183
|
+
<View className="mt-4 gap-4">
|
|
178
184
|
<SignIn />
|
|
179
185
|
<SignUp />
|
|
180
|
-
|
|
186
|
+
</View>
|
|
181
187
|
)}
|
|
182
188
|
{{/if}}
|
|
183
189
|
</Container>
|
|
@@ -1,30 +1,25 @@
|
|
|
1
|
-
import { Container } from "@/components/container";
|
|
2
1
|
import { Link, Stack } from "expo-router";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { Button, Surface } from "heroui-native";
|
|
3
|
+
import { Text, View } from "react-native";
|
|
4
|
+
|
|
5
|
+
import { Container } from "@/components/container";
|
|
5
6
|
|
|
6
7
|
export default function NotFoundScreen() {
|
|
7
8
|
return (
|
|
8
9
|
<>
|
|
9
|
-
<Stack.Screen options=\{{ title: "
|
|
10
|
+
<Stack.Screen options=\{{ title: "Not Found" }} />
|
|
10
11
|
<Container>
|
|
11
|
-
<View className="flex-1 justify-center items-center p-
|
|
12
|
-
<
|
|
13
|
-
<Text className="text-
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
<Text className="text-accent-foreground font-medium text-base">
|
|
23
|
-
Go to Home
|
|
24
|
-
</Text>
|
|
25
|
-
</Pressable>
|
|
26
|
-
</Link>
|
|
27
|
-
</Card>
|
|
12
|
+
<View className="flex-1 justify-center items-center p-4">
|
|
13
|
+
<Surface variant="secondary" className="items-center p-6 max-w-sm rounded-lg">
|
|
14
|
+
<Text className="text-4xl mb-3">🤔</Text>
|
|
15
|
+
<Text className="text-foreground font-medium text-lg mb-1">Page Not Found</Text>
|
|
16
|
+
<Text className="text-muted text-sm text-center mb-4">
|
|
17
|
+
The page you're looking for doesn't exist.
|
|
18
|
+
</Text>
|
|
19
|
+
<Link href="/" asChild>
|
|
20
|
+
<Button size="sm">Go Home</Button>
|
|
21
|
+
</Link>
|
|
22
|
+
</Surface>
|
|
28
23
|
</View>
|
|
29
24
|
</Container>
|
|
30
25
|
</>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Container } from "@/components/container";
|
|
2
|
-
import { Text, View, Pressable } from "react-native";
|
|
3
1
|
import { Ionicons } from "@expo/vector-icons";
|
|
4
2
|
import { router } from "expo-router";
|
|
5
|
-
import {
|
|
3
|
+
import { Button, Surface, useThemeColor } from "heroui-native";
|
|
4
|
+
import { Text, View } from "react-native";
|
|
5
|
+
|
|
6
|
+
import { Container } from "@/components/container";
|
|
6
7
|
|
|
7
8
|
function Modal() {
|
|
8
9
|
const accentForegroundColor = useThemeColor("accent-foreground");
|
|
@@ -13,38 +14,21 @@ function Modal() {
|
|
|
13
14
|
|
|
14
15
|
return (
|
|
15
16
|
<Container>
|
|
16
|
-
<View className="flex-1 justify-center items-center p-
|
|
17
|
-
<
|
|
18
|
-
<
|
|
19
|
-
<View className="w-
|
|
20
|
-
<Ionicons name="checkmark" size={
|
|
17
|
+
<View className="flex-1 justify-center items-center p-4">
|
|
18
|
+
<Surface variant="secondary" className="p-5 w-full max-w-sm rounded-lg">
|
|
19
|
+
<View className="items-center">
|
|
20
|
+
<View className="w-12 h-12 bg-accent rounded-lg items-center justify-center mb-3">
|
|
21
|
+
<Ionicons name="checkmark" size={24} color={accentForegroundColor} />
|
|
21
22
|
</View>
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
</
|
|
29
|
-
</
|
|
30
|
-
|
|
31
|
-
<Pressable
|
|
32
|
-
onPress={handleClose}
|
|
33
|
-
className="bg-accent p-4 rounded-lg w-full active:opacity-70"
|
|
34
|
-
>
|
|
35
|
-
<View className="flex-row items-center justify-center">
|
|
36
|
-
<Text className="text-accent-foreground font-semibold text-base mr-2">
|
|
37
|
-
Close Modal
|
|
38
|
-
</Text>
|
|
39
|
-
<Ionicons
|
|
40
|
-
name="close-circle"
|
|
41
|
-
size={20}
|
|
42
|
-
color={accentForegroundColor}
|
|
43
|
-
/>
|
|
44
|
-
</View>
|
|
45
|
-
</Pressable>
|
|
46
|
-
</Card.Footer>
|
|
47
|
-
</Card>
|
|
23
|
+
<Text className="text-foreground font-medium text-lg mb-1">Modal Screen</Text>
|
|
24
|
+
<Text className="text-muted text-sm text-center mb-4">
|
|
25
|
+
This is an example modal screen for dialogs and confirmations.
|
|
26
|
+
</Text>
|
|
27
|
+
</View>
|
|
28
|
+
<Button onPress={handleClose} className="w-full" size="sm">
|
|
29
|
+
<Button.Label>Close</Button.Label>
|
|
30
|
+
</Button>
|
|
31
|
+
</Surface>
|
|
48
32
|
</View>
|
|
49
33
|
</Container>
|
|
50
34
|
);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "native",
|
|
3
3
|
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
4
5
|
"main": "expo-router/entry",
|
|
5
6
|
"scripts": {
|
|
6
7
|
"start": "expo start",
|
|
@@ -13,7 +14,7 @@
|
|
|
13
14
|
"dependencies": {
|
|
14
15
|
"@expo/metro-runtime": "~6.1.2",
|
|
15
16
|
"@expo/vector-icons": "^15.0.3",
|
|
16
|
-
"@gorhom/bottom-sheet": "^5
|
|
17
|
+
"@gorhom/bottom-sheet": "^5",
|
|
17
18
|
"@react-navigation/drawer": "^7.3.9",
|
|
18
19
|
"@react-navigation/elements": "^2.8.1",
|
|
19
20
|
{{#if (includes examples "ai")}}
|
|
@@ -29,26 +30,25 @@
|
|
|
29
30
|
"expo-router": "~6.0.14",
|
|
30
31
|
"expo-secure-store": "~15.0.7",
|
|
31
32
|
"expo-status-bar": "~3.0.8",
|
|
32
|
-
"heroui-native": "1.0.0-beta.
|
|
33
|
+
"heroui-native": "^1.0.0-beta.9",
|
|
33
34
|
"react": "19.1.0",
|
|
34
35
|
"react-dom": "19.1.0",
|
|
35
36
|
"react-native": "0.81.5",
|
|
36
|
-
"react-native-gesture-handler": "
|
|
37
|
+
"react-native-gesture-handler": "^2.28.0",
|
|
37
38
|
"react-native-keyboard-controller": "1.18.5",
|
|
38
|
-
"react-native-reanimated": "~4.1.
|
|
39
|
-
"react-native-safe-area-context": "5.6.0",
|
|
39
|
+
"react-native-reanimated": "~4.1.1",
|
|
40
|
+
"react-native-safe-area-context": "~5.6.0",
|
|
40
41
|
"react-native-screens": "~4.16.0",
|
|
41
42
|
"react-native-svg": "15.12.1",
|
|
42
43
|
"react-native-web": "^0.21.0",
|
|
43
44
|
"react-native-worklets": "0.5.1",
|
|
44
45
|
"tailwind-merge": "^3.4.0",
|
|
45
|
-
"tailwind-variants": "3.
|
|
46
|
-
"tailwindcss": "
|
|
47
|
-
"uniwind": "^1.
|
|
46
|
+
"tailwind-variants": "^3.2.2",
|
|
47
|
+
"tailwindcss": "^4.1.18",
|
|
48
|
+
"uniwind": "^1.2.2"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@types/node": "^24.10.0",
|
|
51
52
|
"@types/react": "~19.1.0"
|
|
52
|
-
}
|
|
53
|
-
"private": true
|
|
53
|
+
}
|
|
54
54
|
}
|
|
@@ -1,44 +1,40 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import type { NavigationMenuItem } from '@nuxt/ui'
|
|
3
3
|
{{#if (eq auth "better-auth")}}
|
|
4
4
|
import UserMenu from './UserMenu.vue'
|
|
5
5
|
{{/if}}
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
const route = useRoute()
|
|
8
|
+
|
|
9
|
+
const items = computed<NavigationMenuItem[]>(() => [
|
|
10
|
+
{ label: "Home", to: "/", active: route.path === "/" },
|
|
9
11
|
{{#if (or (eq auth "better-auth") (eq auth "clerk"))}}
|
|
10
|
-
{ to: "/dashboard",
|
|
12
|
+
{ label: "Dashboard", to: "/dashboard", active: route.path.startsWith("/dashboard") },
|
|
11
13
|
{{/if}}
|
|
12
14
|
{{#if (includes examples "todo")}}
|
|
13
|
-
{ to: "/todos",
|
|
15
|
+
{ label: "Todos", to: "/todos", active: route.path.startsWith("/todos") },
|
|
14
16
|
{{/if}}
|
|
15
17
|
{{#if (includes examples "ai")}}
|
|
16
|
-
{ to: "/ai",
|
|
18
|
+
{ label: "AI Chat", to: "/ai", active: route.path.startsWith("/ai") },
|
|
17
19
|
{{/if}}
|
|
18
|
-
]
|
|
20
|
+
])
|
|
19
21
|
</script>
|
|
20
22
|
|
|
21
23
|
<template>
|
|
22
|
-
<
|
|
23
|
-
<
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<UserMenu />
|
|
39
|
-
{{/if}}
|
|
40
|
-
</div>
|
|
41
|
-
</div>
|
|
42
|
-
<USeparator />
|
|
43
|
-
</div>
|
|
24
|
+
<UHeader>
|
|
25
|
+
<template #left>
|
|
26
|
+
<UNavigationMenu :items="items" />
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<template #right>
|
|
30
|
+
<UColorModeButton />
|
|
31
|
+
{{#if (eq auth "better-auth")}}
|
|
32
|
+
<UserMenu />
|
|
33
|
+
{{/if}}
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<template #body>
|
|
37
|
+
<UNavigationMenu :items="items" orientation="vertical" class="-mx-2.5" />
|
|
38
|
+
</template>
|
|
39
|
+
</UHeader>
|
|
44
40
|
</template>
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
</script>
|
|
1
|
+
<script setup></script>
|
|
3
2
|
|
|
4
3
|
<template>
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
<div class="grid grid-rows-[auto_1fr] h-svh">
|
|
5
|
+
<Header />
|
|
6
|
+
<UMain>
|
|
7
|
+
<slot />
|
|
8
|
+
</UMain>
|
|
9
|
+
</div>
|
|
11
10
|
</template>
|
|
@@ -4,8 +4,8 @@ import { api } from "@{{ projectName }}/backend/convex/_generated/api";
|
|
|
4
4
|
import { useConvexQuery } from "convex-vue";
|
|
5
5
|
{{else}}
|
|
6
6
|
{{#unless (eq api "none")}}
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const { $orpc } = useNuxtApp()
|
|
8
|
+
import { useQuery } from '@tanstack/vue-query'
|
|
9
9
|
{{/unless}}
|
|
10
10
|
{{/if}}
|
|
11
11
|
|
|
@@ -29,50 +29,69 @@ const TITLE_TEXT = `
|
|
|
29
29
|
const healthCheck = useConvexQuery(api.healthCheck.get, {});
|
|
30
30
|
{{else}}
|
|
31
31
|
{{#unless (eq api "none")}}
|
|
32
|
-
|
|
32
|
+
const healthCheck = useQuery($orpc.healthCheck.queryOptions())
|
|
33
33
|
{{/unless}}
|
|
34
34
|
{{/if}}
|
|
35
35
|
</script>
|
|
36
36
|
|
|
37
37
|
<template>
|
|
38
|
-
<
|
|
38
|
+
<UContainer class="py-8">
|
|
39
39
|
<pre class="overflow-x-auto font-mono text-sm whitespace-pre-wrap">\{{ TITLE_TEXT }}</pre>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
|
|
41
|
+
<div class="grid gap-6 mt-6">
|
|
42
|
+
<UCard>
|
|
43
|
+
<template #header>
|
|
44
|
+
<div class="font-medium">API Status</div>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
{{#if (eq backend "convex")}}
|
|
48
|
+
<div class="flex items-center gap-2">
|
|
49
|
+
<UIcon
|
|
50
|
+
:name="healthCheck === undefined ? 'i-lucide-loader-2' : healthCheck.data.value === 'OK' ? 'i-lucide-check-circle' : 'i-lucide-x-circle'"
|
|
51
|
+
:class="[
|
|
52
|
+
healthCheck === undefined ? 'animate-spin text-muted' : '',
|
|
53
|
+
healthCheck?.data.value === 'OK' ? 'text-success' : 'text-error'
|
|
54
|
+
]"
|
|
55
|
+
/>
|
|
56
|
+
<span class="text-sm">
|
|
57
|
+
\{{
|
|
58
|
+
healthCheck === undefined
|
|
59
|
+
? "Checking..."
|
|
60
|
+
: healthCheck.data.value === "OK"
|
|
61
|
+
? "Connected"
|
|
62
|
+
: "Error"
|
|
63
|
+
}}
|
|
64
|
+
</span>
|
|
65
|
+
</div>
|
|
66
|
+
{{else}}
|
|
67
|
+
{{#unless (eq api "none")}}
|
|
43
68
|
<div class="flex items-center gap-2">
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
</template>
|
|
67
|
-
<template v-else>
|
|
68
|
-
Idle
|
|
69
|
-
</template>
|
|
70
|
-
</span>
|
|
71
|
-
</div>
|
|
72
|
-
{{/unless}}
|
|
73
|
-
{{/if}}
|
|
69
|
+
<UIcon
|
|
70
|
+
:name="healthCheck.isLoading.value ? 'i-lucide-loader-2' : healthCheck.isSuccess.value ? 'i-lucide-check-circle' : 'i-lucide-x-circle'"
|
|
71
|
+
:class="[
|
|
72
|
+
healthCheck.isLoading.value ? 'animate-spin text-muted' : '',
|
|
73
|
+
healthCheck.isSuccess.value ? 'text-success' : '',
|
|
74
|
+
healthCheck.isError.value ? 'text-error' : ''
|
|
75
|
+
]"
|
|
76
|
+
/>
|
|
77
|
+
<span class="text-sm">
|
|
78
|
+
<template v-if="healthCheck.isLoading.value">
|
|
79
|
+
Checking...
|
|
80
|
+
</template>
|
|
81
|
+
<template v-else-if="healthCheck.isSuccess.value">
|
|
82
|
+
Connected (\{{ healthCheck.data.value }})
|
|
83
|
+
</template>
|
|
84
|
+
<template v-else-if="healthCheck.isError.value">
|
|
85
|
+
Error: \{{ healthCheck.error.value?.message || 'Failed to connect' }}
|
|
86
|
+
</template>
|
|
87
|
+
<template v-else>
|
|
88
|
+
Idle
|
|
89
|
+
</template>
|
|
90
|
+
</span>
|
|
74
91
|
</div>
|
|
75
|
-
|
|
92
|
+
{{/unless}}
|
|
93
|
+
{{/if}}
|
|
94
|
+
</UCard>
|
|
76
95
|
</div>
|
|
77
|
-
</
|
|
96
|
+
</UContainer>
|
|
78
97
|
</template>
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { cn } from
|
|
1
|
+
import { cn } from "@/lib/utils";
|
|
2
2
|
|
|
3
|
-
function Skeleton({ className, ...props }: React.
|
|
3
|
+
function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
4
4
|
return (
|
|
5
5
|
<div
|
|
6
6
|
data-slot="skeleton"
|
|
7
|
-
className={cn(
|
|
7
|
+
className={cn("bg-muted rounded-none animate-pulse", className)}
|
|
8
8
|
{...props}
|
|
9
9
|
/>
|
|
10
|
-
)
|
|
10
|
+
);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export { Skeleton }
|
|
13
|
+
export { Skeleton };
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed } from "vue";
|
|
3
|
-
|
|
4
|
-
const colorMode = useColorMode()
|
|
5
|
-
|
|
6
|
-
const isDark = computed({
|
|
7
|
-
get () {
|
|
8
|
-
return colorMode.value === 'dark'
|
|
9
|
-
},
|
|
10
|
-
set (value) {
|
|
11
|
-
colorMode.preference = value ? 'dark' : 'light'
|
|
12
|
-
}
|
|
13
|
-
})
|
|
14
|
-
</script>
|
|
15
|
-
|
|
16
|
-
<template>
|
|
17
|
-
<div class="flex items-center">
|
|
18
|
-
<USwitch
|
|
19
|
-
v-model="isDark"
|
|
20
|
-
:checked-icon="isDark ? 'i-lucide-moon' : ''"
|
|
21
|
-
:unchecked-icon="!isDark ? 'i-lucide-sun' : ''"
|
|
22
|
-
class="mr-2"
|
|
23
|
-
/>
|
|
24
|
-
</div>
|
|
25
|
-
</template>
|