create-better-t-stack 2.0.12 → 2.1.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.
Files changed (124) hide show
  1. package/dist/index.js +81 -81
  2. package/package.json +1 -1
  3. package/templates/api/orpc/native/utils/orpc.ts.hbs +49 -0
  4. package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +35 -0
  5. package/templates/{auth/native/utils/trpc.ts → api/trpc/native/utils/trpc.ts.hbs} +4 -0
  6. package/templates/api/trpc/web/{base → react/base}/src/utils/trpc.ts.hbs +1 -1
  7. package/templates/auth/native/app/(drawer)/{index.tsx → index.tsx.hbs} +11 -0
  8. package/templates/auth/native/components/{sign-in.tsx → sign-in.tsx.hbs} +6 -1
  9. package/templates/auth/native/components/{sign-up.tsx → sign-up.tsx.hbs} +5 -0
  10. package/templates/auth/server/base/src/lib/auth.ts.hbs +48 -9
  11. package/templates/auth/web/nuxt/app/components/SignInForm.vue +78 -0
  12. package/templates/auth/web/nuxt/app/components/SignUpForm.vue +85 -0
  13. package/templates/auth/web/nuxt/app/components/UserMenu.vue +43 -0
  14. package/templates/auth/web/nuxt/app/middleware/auth.ts +12 -0
  15. package/templates/auth/web/nuxt/app/pages/dashboard.vue +27 -0
  16. package/templates/auth/web/nuxt/app/pages/login.vue +24 -0
  17. package/templates/auth/web/nuxt/app/plugins/auth-client.ts +16 -0
  18. package/templates/examples/ai/web/nuxt/app/pages/ai.vue +64 -0
  19. package/templates/examples/todo/web/nuxt/app/pages/todos.vue +108 -0
  20. package/templates/extras/_npmrc.hbs +5 -0
  21. package/templates/frontend/native/app/(drawer)/{index.tsx → index.tsx.hbs} +10 -0
  22. package/templates/frontend/native/app/{_layout.tsx → _layout.tsx.hbs} +8 -3
  23. package/templates/frontend/native/package.json +0 -3
  24. package/templates/frontend/nuxt/_gitignore +24 -0
  25. package/templates/frontend/nuxt/app/app.config.ts +15 -0
  26. package/templates/frontend/nuxt/app/app.vue +13 -0
  27. package/templates/frontend/nuxt/app/assets/css/main.css +2 -0
  28. package/templates/frontend/nuxt/app/components/Header.vue.hbs +45 -0
  29. package/templates/frontend/nuxt/app/components/Loader.vue +5 -0
  30. package/templates/frontend/nuxt/app/components/ModeToggle.vue +23 -0
  31. package/templates/frontend/nuxt/app/layouts/default.vue +11 -0
  32. package/templates/frontend/nuxt/app/pages/index.vue +63 -0
  33. package/templates/frontend/nuxt/app/plugins/vue-query.ts +44 -0
  34. package/templates/frontend/nuxt/nuxt.config.ts.hbs +19 -0
  35. package/templates/frontend/nuxt/package.json +25 -0
  36. package/templates/frontend/nuxt/public/favicon.ico +0 -0
  37. package/templates/frontend/nuxt/public/robots.txt +2 -0
  38. package/templates/frontend/nuxt/server/tsconfig.json +3 -0
  39. package/templates/frontend/nuxt/tsconfig.json +4 -0
  40. package/templates/extras/_npmrc +0 -1
  41. package/templates/frontend/native/utils/trpc.ts +0 -19
  42. /package/templates/api/orpc/web/{base → react/base}/src/utils/orpc.ts.hbs +0 -0
  43. /package/templates/auth/web/{base → react/base}/src/lib/auth-client.ts.hbs +0 -0
  44. /package/templates/auth/web/{next → react/next}/src/app/dashboard/page.tsx.hbs +0 -0
  45. /package/templates/auth/web/{next → react/next}/src/app/login/page.tsx +0 -0
  46. /package/templates/auth/web/{next → react/next}/src/components/sign-in-form.tsx +0 -0
  47. /package/templates/auth/web/{next → react/next}/src/components/sign-up-form.tsx +0 -0
  48. /package/templates/auth/web/{next → react/next}/src/components/theme-provider.tsx +0 -0
  49. /package/templates/auth/web/{next → react/next}/src/components/user-menu.tsx +0 -0
  50. /package/templates/auth/web/{react-router → react/react-router}/src/components/sign-in-form.tsx +0 -0
  51. /package/templates/auth/web/{react-router → react/react-router}/src/components/sign-up-form.tsx +0 -0
  52. /package/templates/auth/web/{react-router → react/react-router}/src/components/user-menu.tsx +0 -0
  53. /package/templates/auth/web/{react-router → react/react-router}/src/routes/dashboard.tsx.hbs +0 -0
  54. /package/templates/auth/web/{react-router → react/react-router}/src/routes/login.tsx +0 -0
  55. /package/templates/auth/web/{tanstack-router → react/tanstack-router}/src/components/sign-in-form.tsx +0 -0
  56. /package/templates/auth/web/{tanstack-router → react/tanstack-router}/src/components/sign-up-form.tsx +0 -0
  57. /package/templates/auth/web/{tanstack-router → react/tanstack-router}/src/components/user-menu.tsx +0 -0
  58. /package/templates/auth/web/{tanstack-router → react/tanstack-router}/src/routes/dashboard.tsx.hbs +0 -0
  59. /package/templates/auth/web/{tanstack-router → react/tanstack-router}/src/routes/login.tsx +0 -0
  60. /package/templates/auth/web/{tanstack-start → react/tanstack-start}/src/components/sign-in-form.tsx +0 -0
  61. /package/templates/auth/web/{tanstack-start → react/tanstack-start}/src/components/sign-up-form.tsx +0 -0
  62. /package/templates/auth/web/{tanstack-start → react/tanstack-start}/src/components/user-menu.tsx +0 -0
  63. /package/templates/auth/web/{tanstack-start → react/tanstack-start}/src/routes/dashboard.tsx.hbs +0 -0
  64. /package/templates/auth/web/{tanstack-start → react/tanstack-start}/src/routes/login.tsx +0 -0
  65. /package/templates/examples/ai/web/{react-router → react/react-router}/src/routes/ai.tsx +0 -0
  66. /package/templates/examples/ai/web/{tanstack-router → react/tanstack-router}/src/routes/ai.tsx +0 -0
  67. /package/templates/examples/ai/web/{tanstack-start → react/tanstack-start}/src/routes/ai.tsx +0 -0
  68. /package/templates/examples/todo/web/{react-router → react/react-router}/src/routes/todos.tsx.hbs +0 -0
  69. /package/templates/examples/todo/web/{tanstack-router → react/tanstack-router}/src/routes/todos.tsx.hbs +0 -0
  70. /package/templates/examples/todo/web/{tanstack-start → react/tanstack-start}/src/routes/todos.tsx.hbs +0 -0
  71. /package/templates/frontend/{next → react/next}/next-env.d.ts +0 -0
  72. /package/templates/frontend/{next → react/next}/next.config.ts +0 -0
  73. /package/templates/frontend/{next → react/next}/package.json +0 -0
  74. /package/templates/frontend/{next → react/next}/postcss.config.mjs +0 -0
  75. /package/templates/frontend/{next → react/next}/src/app/favicon.ico +0 -0
  76. /package/templates/frontend/{next → react/next}/src/app/layout.tsx +0 -0
  77. /package/templates/frontend/{next → react/next}/src/app/page.tsx.hbs +0 -0
  78. /package/templates/frontend/{next → react/next}/src/components/mode-toggle.tsx +0 -0
  79. /package/templates/frontend/{next → react/next}/src/components/providers.tsx.hbs +0 -0
  80. /package/templates/frontend/{next → react/next}/src/components/theme-provider.tsx +0 -0
  81. /package/templates/frontend/{next → react/next}/tsconfig.json +0 -0
  82. /package/templates/frontend/{react-router → react/react-router}/package.json +0 -0
  83. /package/templates/frontend/{react-router → react/react-router}/public/favicon.ico +0 -0
  84. /package/templates/frontend/{react-router → react/react-router}/react-router.config.ts +0 -0
  85. /package/templates/frontend/{react-router → react/react-router}/src/components/mode-toggle.tsx +0 -0
  86. /package/templates/frontend/{react-router → react/react-router}/src/components/theme-provider.tsx +0 -0
  87. /package/templates/frontend/{react-router → react/react-router}/src/root.tsx.hbs +0 -0
  88. /package/templates/frontend/{react-router → react/react-router}/src/routes/_index.tsx.hbs +0 -0
  89. /package/templates/frontend/{react-router → react/react-router}/src/routes.ts +0 -0
  90. /package/templates/frontend/{react-router → react/react-router}/tsconfig.json +0 -0
  91. /package/templates/frontend/{react-router → react/react-router}/vite.config.ts.hbs +0 -0
  92. /package/templates/frontend/{tanstack-router → react/tanstack-router}/index.html +0 -0
  93. /package/templates/frontend/{tanstack-router → react/tanstack-router}/package.json +0 -0
  94. /package/templates/frontend/{tanstack-router → react/tanstack-router}/src/components/mode-toggle.tsx +0 -0
  95. /package/templates/frontend/{tanstack-router → react/tanstack-router}/src/components/theme-provider.tsx +0 -0
  96. /package/templates/frontend/{tanstack-router → react/tanstack-router}/src/main.tsx.hbs +0 -0
  97. /package/templates/frontend/{tanstack-router → react/tanstack-router}/src/routes/__root.tsx.hbs +0 -0
  98. /package/templates/frontend/{tanstack-router → react/tanstack-router}/src/routes/index.tsx.hbs +0 -0
  99. /package/templates/frontend/{tanstack-router → react/tanstack-router}/tsconfig.json +0 -0
  100. /package/templates/frontend/{tanstack-router → react/tanstack-router}/vite.config.ts.hbs +0 -0
  101. /package/templates/frontend/{tanstack-start → react/tanstack-start}/app.config.ts +0 -0
  102. /package/templates/frontend/{tanstack-start → react/tanstack-start}/package.json +0 -0
  103. /package/templates/frontend/{tanstack-start → react/tanstack-start}/public/robots.txt +0 -0
  104. /package/templates/frontend/{tanstack-start → react/tanstack-start}/src/api.ts +0 -0
  105. /package/templates/frontend/{tanstack-start → react/tanstack-start}/src/client.tsx +0 -0
  106. /package/templates/frontend/{tanstack-start → react/tanstack-start}/src/router.tsx.hbs +0 -0
  107. /package/templates/frontend/{tanstack-start → react/tanstack-start}/src/routes/__root.tsx.hbs +0 -0
  108. /package/templates/frontend/{tanstack-start → react/tanstack-start}/src/routes/index.tsx.hbs +0 -0
  109. /package/templates/frontend/{tanstack-start → react/tanstack-start}/src/ssr.tsx +0 -0
  110. /package/templates/frontend/{tanstack-start → react/tanstack-start}/tsconfig.json +0 -0
  111. /package/templates/frontend/{web-base → react/web-base}/_gitignore +0 -0
  112. /package/templates/frontend/{web-base → react/web-base}/components.json +0 -0
  113. /package/templates/frontend/{web-base → react/web-base}/src/components/header.tsx.hbs +0 -0
  114. /package/templates/frontend/{web-base → react/web-base}/src/components/loader.tsx +0 -0
  115. /package/templates/frontend/{web-base → react/web-base}/src/components/ui/button.tsx +0 -0
  116. /package/templates/frontend/{web-base → react/web-base}/src/components/ui/card.tsx +0 -0
  117. /package/templates/frontend/{web-base → react/web-base}/src/components/ui/checkbox.tsx +0 -0
  118. /package/templates/frontend/{web-base → react/web-base}/src/components/ui/dropdown-menu.tsx +0 -0
  119. /package/templates/frontend/{web-base → react/web-base}/src/components/ui/input.tsx +0 -0
  120. /package/templates/frontend/{web-base → react/web-base}/src/components/ui/label.tsx +0 -0
  121. /package/templates/frontend/{web-base → react/web-base}/src/components/ui/skeleton.tsx +0 -0
  122. /package/templates/frontend/{web-base → react/web-base}/src/components/ui/sonner.tsx +0 -0
  123. /package/templates/frontend/{web-base → react/web-base}/src/index.css +0 -0
  124. /package/templates/frontend/{web-base → react/web-base}/src/lib/utils.ts +0 -0
@@ -0,0 +1,108 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/vue-query'
4
+
5
+ const { $orpc } = useNuxtApp()
6
+
7
+ const newTodoText = ref('')
8
+ const queryClient = useQueryClient()
9
+
10
+ const todos = useQuery($orpc.todo.getAll.queryOptions())
11
+
12
+ const createMutation = useMutation($orpc.todo.create.mutationOptions({
13
+ onSuccess: () => {
14
+ queryClient.invalidateQueries()
15
+ newTodoText.value = ''
16
+ }
17
+ }))
18
+
19
+ const toggleMutation = useMutation($orpc.todo.toggle.mutationOptions({
20
+ onSuccess: () => queryClient.invalidateQueries()
21
+ }))
22
+
23
+ const deleteMutation = useMutation($orpc.todo.delete.mutationOptions({
24
+ onSuccess: () => queryClient.invalidateQueries()
25
+ }))
26
+
27
+ function handleAddTodo() {
28
+ if (newTodoText.value.trim()) {
29
+ createMutation.mutate({ text: newTodoText.value })
30
+ }
31
+ }
32
+
33
+ function handleToggleTodo(id: number, completed: boolean) {
34
+ toggleMutation.mutate({ id, completed: !completed })
35
+ }
36
+
37
+ function handleDeleteTodo(id: number) {
38
+ deleteMutation.mutate({ id })
39
+ }
40
+ </script>
41
+
42
+ <template>
43
+ <div class="mx-auto w-full max-w-md py-10">
44
+ <UCard>
45
+ <template #header>
46
+ <div>
47
+ <div class="text-xl font-bold">Todo List</div>
48
+ <div class="text-muted text-sm">Manage your tasks efficiently</div>
49
+ </div>
50
+ </template>
51
+ <form @submit.prevent="handleAddTodo" class="mb-6 flex items-center gap-2">
52
+ <UInput
53
+ v-model="newTodoText"
54
+ placeholder="Add a new task..."
55
+ autocomplete="off"
56
+ class="w-full"
57
+ />
58
+ <UButton
59
+ type="submit"
60
+ icon="i-lucide-plus"
61
+ >
62
+ Add
63
+ </UButton>
64
+ </form>
65
+
66
+ <div v-if="todos.status.value === 'pending'" class="flex justify-center py-4">
67
+ <UIcon name="i-lucide-loader-2" class="animate-spin w-6 h-6" />
68
+ </div>
69
+ <p v-else-if="todos.status.value === 'error'" class="py-4 text-center text-red-500">
70
+ Error: {{ todos.error.value?.message || 'Failed to load todos' }}
71
+ </p>
72
+ <p v-else-if="todos.data.value?.length === 0" class="py-4 text-center">
73
+ No todos yet. Add one above!
74
+ </p>
75
+ <ul v-else class="space-y-2">
76
+ <li
77
+ v-for="todo in todos.data.value"
78
+ :key="todo.id"
79
+ class="flex items-center justify-between rounded-md border p-2"
80
+ >
81
+ <div class="flex items-center gap-2">
82
+ <UCheckbox
83
+ :model-value="todo.completed"
84
+ @update:model-value="() => handleToggleTodo(todo.id, todo.completed)"
85
+ :id="`todo-${todo.id}`"
86
+ />
87
+ <label
88
+ :for="`todo-${todo.id}`"
89
+ :class="{ 'line-through text-muted': todo.completed }"
90
+ class="cursor-pointer"
91
+ >
92
+ {{ todo.text }}
93
+ </label>
94
+ </div>
95
+ <UButton
96
+ color="neutral"
97
+ variant="ghost"
98
+ size="sm"
99
+ square
100
+ @click="handleDeleteTodo(todo.id)"
101
+ aria-label="Delete todo"
102
+ icon="i-lucide-trash-2"
103
+ />
104
+ </li>
105
+ </ul>
106
+ </UCard>
107
+ </div>
108
+ </template>
@@ -0,0 +1,5 @@
1
+ node-linker=hoisted
2
+ {{#if (includes frontend "nuxt")}}
3
+ shamefully-hoist=true
4
+ strict-peer-dependencies=false
5
+ {{/if}}
@@ -1,10 +1,20 @@
1
1
  import { useQuery } from "@tanstack/react-query";
2
2
  import { View, Text, ScrollView } from "react-native";
3
3
  import { Container } from "@/components/container";
4
+ {{#if (eq api "orpc")}}
5
+ import { orpc } from "@/utils/orpc";
6
+ {{/if}}
7
+ {{#if (eq api "trpc")}}
4
8
  import { trpc } from "@/utils/trpc";
9
+ {{/if}}
5
10
 
6
11
  export default function Home() {
12
+ {{#if (eq api "orpc")}}
13
+ const healthCheck = useQuery(orpc.healthCheck.queryOptions());
14
+ {{/if}}
15
+ {{#if (eq api "trpc")}}
7
16
  const healthCheck = useQuery(trpc.healthCheck.queryOptions());
17
+ {{/if}}
8
18
 
9
19
  return (
10
20
  <Container>
@@ -9,7 +9,12 @@ import {
9
9
  import { StatusBar } from "expo-status-bar";
10
10
  import { GestureHandlerRootView } from "react-native-gesture-handler";
11
11
  import "../global.css";
12
+ {{#if (eq api "trpc")}}
12
13
  import { queryClient } from "@/utils/trpc";
14
+ {{/if}}
15
+ {{#if (eq api "orpc")}}
16
+ import { queryClient } from "@/utils/orpc";
17
+ {{/if}}
13
18
  import { NAV_THEME } from "@/lib/constants";
14
19
  import React, { useRef } from "react";
15
20
  import { useColorScheme } from "@/lib/use-color-scheme";
@@ -56,12 +61,12 @@ export default function RootLayout() {
56
61
  <QueryClientProvider client={queryClient}>
57
62
  <ThemeProvider value={isDarkColorScheme ? DARK_THEME : LIGHT_THEME}>
58
63
  <StatusBar style={isDarkColorScheme ? "light" : "dark"} />
59
- <GestureHandlerRootView style={{ flex: 1 }}>
64
+ <GestureHandlerRootView style=\{{ flex: 1 }}>
60
65
  <Stack>
61
- <Stack.Screen name="(drawer)" options={{ headerShown: false }} />
66
+ <Stack.Screen name="(drawer)" options=\{{ headerShown: false }} />
62
67
  <Stack.Screen
63
68
  name="modal"
64
- options={{ title: "Modal", presentation: "modal" }}
69
+ options=\{{ title: "Modal", presentation: "modal" }}
65
70
  />
66
71
  </Stack>
67
72
  </GestureHandlerRootView>
@@ -16,9 +16,6 @@
16
16
  "@react-navigation/native": "^7.0.14",
17
17
  "@tanstack/react-form": "^1.0.5",
18
18
  "@tanstack/react-query": "^5.69.2",
19
- "@trpc/client": "^11.0.0",
20
- "@trpc/server": "^11.0.0",
21
- "@trpc/tanstack-react-query": "^11.0.0",
22
19
  "expo": "^52.0.44",
23
20
  "expo-constants": "~17.0.8",
24
21
  "expo-linking": "~7.0.5",
@@ -0,0 +1,24 @@
1
+ # Nuxt dev/build outputs
2
+ .output
3
+ .data
4
+ .nuxt
5
+ .nitro
6
+ .cache
7
+ dist
8
+
9
+ # Node dependencies
10
+ node_modules
11
+
12
+ # Logs
13
+ logs
14
+ *.log
15
+
16
+ # Misc
17
+ .DS_Store
18
+ .fleet
19
+ .idea
20
+
21
+ # Local env files
22
+ .env
23
+ .env.*
24
+ !.env.example
@@ -0,0 +1,15 @@
1
+ export default defineAppConfig({
2
+ // https://ui.nuxt.com/getting-started/theme#design-system
3
+ ui: {
4
+ colors: {
5
+ primary: 'emerald',
6
+ neutral: 'slate',
7
+ },
8
+ button: {
9
+ defaultVariants: {
10
+ // Set default button color to neutral
11
+ // color: 'neutral'
12
+ }
13
+ }
14
+ }
15
+ })
@@ -0,0 +1,13 @@
1
+ <script setup lang="ts">
2
+ import { VueQueryDevtools } from '@tanstack/vue-query-devtools'
3
+ </script>
4
+
5
+ <template>
6
+ <NuxtLoadingIndicator />
7
+ <UApp>
8
+ <NuxtLayout>
9
+ <NuxtPage />
10
+ </NuxtLayout>
11
+ </UApp>
12
+ <VueQueryDevtools />
13
+ </template>
@@ -0,0 +1,2 @@
1
+ @import "tailwindcss";
2
+ @import "@nuxt/ui";
@@ -0,0 +1,45 @@
1
+ <script setup lang="ts">
2
+ import { USeparator } from '#components';
3
+ import ModeToggle from './ModeToggle.vue'
4
+ {{#if auth}}
5
+ import UserMenu from './UserMenu.vue'
6
+ {{/if}}
7
+
8
+ const links = [
9
+ { to: "/", label: "Home" },
10
+ {{#if auth}}
11
+ { to: "/dashboard", label: "Dashboard" },
12
+ {{/if}}
13
+ {{#if (includes examples "todo")}}
14
+ { to: "/todos", label: "Todos" },
15
+ {{/if}}
16
+ {{#if (includes examples "ai")}}
17
+ { to: "/ai", label: "AI Chat" },
18
+ {{/if}}
19
+ ];
20
+ </script>
21
+
22
+ <template>
23
+ <div>
24
+ <div class="flex flex-row items-center justify-between px-2 py-1">
25
+ <nav class="flex gap-4 text-lg">
26
+ <NuxtLink
27
+ v-for="link in links"
28
+ :key="link.to"
29
+ :to="link.to"
30
+ class="text-foreground hover:text-primary"
31
+ active-class="text-primary font-semibold"
32
+ >
33
+ \{{ link.label }}
34
+ </NuxtLink>
35
+ </nav>
36
+ <div class="flex items-center gap-2">
37
+ <ModeToggle />
38
+ {{#if auth}}
39
+ <UserMenu />
40
+ {{/if}}
41
+ </div>
42
+ </div>
43
+ <USeparator />
44
+ </div>
45
+ </template>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <div class="flex h-full items-center justify-center pt-8">
3
+ <UIcon name="i-lucide-loader-2" class="animate-spin text-2xl" />
4
+ </div>
5
+ </template>
@@ -0,0 +1,23 @@
1
+ <script setup lang="ts">
2
+ const colorMode = useColorMode()
3
+
4
+ const isDark = computed({
5
+ get () {
6
+ return colorMode.value === 'dark'
7
+ },
8
+ set (value) {
9
+ colorMode.preference = value ? 'dark' : 'light'
10
+ }
11
+ })
12
+ </script>
13
+
14
+ <template>
15
+ <div class="flex items-center">
16
+ <USwitch
17
+ v-model="isDark"
18
+ :checked-icon="isDark ? 'i-lucide-moon' : ''"
19
+ :unchecked-icon="!isDark ? 'i-lucide-sun' : ''"
20
+ class="mr-2"
21
+ />
22
+ </div>
23
+ </template>
@@ -0,0 +1,11 @@
1
+ <script setup>
2
+ </script>
3
+
4
+ <template>
5
+ <div class="grid grid-rows-[auto_1fr] h-full">
6
+ <Header />
7
+ <main class="overflow-y-auto">
8
+ <slot />
9
+ </main>
10
+ </div>
11
+ </template>
@@ -0,0 +1,63 @@
1
+ <script setup lang="ts">
2
+ const { $orpc } = useNuxtApp()
3
+ import { useQuery } from '@tanstack/vue-query'
4
+
5
+ const TITLE_TEXT = `
6
+ ██████╗ ███████╗████████╗████████╗███████╗██████╗
7
+ ██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗
8
+ ██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝
9
+ ██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗
10
+ ██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║
11
+ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
12
+
13
+ ████████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗
14
+ ╚══██╔══╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
15
+ ██║ ███████╗ ██║ ███████║██║ █████╔╝
16
+ ██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗
17
+ ██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗
18
+ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
19
+ `;
20
+
21
+ const healthCheck = useQuery($orpc.healthCheck.queryOptions())
22
+
23
+ </script>
24
+
25
+ <template>
26
+ <div class="container mx-auto max-w-3xl px-4 py-2">
27
+ <pre class="overflow-x-auto font-mono text-sm whitespace-pre-wrap">{{ TITLE_TEXT }}</pre>
28
+ <div class="grid gap-6 mt-4">
29
+ <section class="rounded-lg border p-4">
30
+ <h2 class="mb-2 font-medium">API Status</h2>
31
+ <div class="flex items-center gap-2">
32
+ <div class="flex items-center gap-2">
33
+ <div
34
+ class="w-2 h-2 rounded-full"
35
+ :class="{
36
+ 'bg-yellow-500 animate-pulse': healthCheck.status.value === 'pending',
37
+ 'bg-green-500': healthCheck.status.value === 'success',
38
+ 'bg-red-500': healthCheck.status.value === 'error',
39
+ 'bg-gray-400': healthCheck.status.value !== 'pending' &&
40
+ healthCheck.status.value !== 'success' &&
41
+ healthCheck.status.value !== 'error'
42
+ }"
43
+ ></div>
44
+ <span class="text-sm text-muted-foreground">
45
+ <template v-if="healthCheck.status.value === 'pending'">
46
+ Checking...
47
+ </template>
48
+ <template v-else-if="healthCheck.status.value === 'success'">
49
+ Connected ({{ healthCheck.data.value }})
50
+ </template>
51
+ <template v-else-if="healthCheck.status.value === 'error'">
52
+ Error: {{ healthCheck.error.value?.message || 'Failed to connect' }}
53
+ </template>
54
+ <template v-else>
55
+ Idle
56
+ </template>
57
+ </span>
58
+ </div>
59
+ </div>
60
+ </section>
61
+ </div>
62
+ </div>
63
+ </template>
@@ -0,0 +1,44 @@
1
+ import type {
2
+ DehydratedState,
3
+ VueQueryPluginOptions,
4
+ } from '@tanstack/vue-query'
5
+ import {
6
+ dehydrate,
7
+ hydrate,
8
+ QueryCache,
9
+ QueryClient,
10
+ VueQueryPlugin,
11
+ } from '@tanstack/vue-query'
12
+
13
+ export default defineNuxtPlugin((nuxt) => {
14
+ const vueQueryState = useState<DehydratedState | null>('vue-query')
15
+
16
+ const toast = useToast()
17
+
18
+ const queryClient = new QueryClient({
19
+ queryCache: new QueryCache({
20
+ onError: (error) => {
21
+ console.log(error)
22
+ toast.add({
23
+ title: 'Error',
24
+ description: error?.message || 'An unexpected error occurred.',
25
+ })
26
+ },
27
+ }),
28
+ })
29
+ const options: VueQueryPluginOptions = { queryClient }
30
+
31
+ nuxt.vueApp.use(VueQueryPlugin, options)
32
+
33
+ if (import.meta.server) {
34
+ nuxt.hooks.hook('app:rendered', () => {
35
+ vueQueryState.value = dehydrate(queryClient)
36
+ })
37
+ }
38
+
39
+ if (import.meta.client) {
40
+ nuxt.hooks.hook('app:created', () => {
41
+ hydrate(queryClient, vueQueryState.value)
42
+ })
43
+ }
44
+ })
@@ -0,0 +1,19 @@
1
+ // https://nuxt.com/docs/api/configuration/nuxt-config
2
+ export default defineNuxtConfig({
3
+ compatibilityDate: '2024-11-01',
4
+ future: {
5
+ compatibilityVersion: 4
6
+ },
7
+ devtools: { enabled: true },
8
+ modules: ['@nuxt/ui'],
9
+ css: ['~/assets/css/main.css'],
10
+ devServer: {
11
+ port: 3001
12
+ },
13
+ ssr: false,
14
+ runtimeConfig: {
15
+ public: {
16
+ serverURL: process.env.NUXT_PUBLIC_SERVER_URL,
17
+ }
18
+ }
19
+ })
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "web",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "nuxt build",
7
+ "dev": "nuxt dev",
8
+ "generate": "nuxt generate",
9
+ "preview": "nuxt preview",
10
+ "postinstall": "nuxt prepare"
11
+ },
12
+ "dependencies": {
13
+ "@nuxt/ui": "3.0.2",
14
+ "@tanstack/vue-query": "^5.74.5",
15
+ "nuxt": "^3.16.2",
16
+ "typescript": "^5.6.3",
17
+ "vue": "^3.5.13",
18
+ "vue-router": "^4.5.0",
19
+ "zod": "^3.24.3"
20
+ },
21
+ "devDependencies": {
22
+ "@tanstack/vue-query-devtools": "^5.74.5",
23
+ "@iconify-json/lucide": "^1.2.38"
24
+ }
25
+ }
@@ -0,0 +1,2 @@
1
+ User-Agent: *
2
+ Disallow:
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "../.nuxt/tsconfig.server.json"
3
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ // https://nuxt.com/docs/guide/concepts/typescript
3
+ "extends": "./.nuxt/tsconfig.json"
4
+ }
@@ -1 +0,0 @@
1
- node-linker=hoisted
@@ -1,19 +0,0 @@
1
- import type { AppRouter } from "../../server/src/routers";
2
- import { QueryClient } from "@tanstack/react-query";
3
- import { createTRPCClient, httpBatchLink } from "@trpc/client";
4
- import { createTRPCOptionsProxy } from "@trpc/tanstack-react-query";
5
-
6
- export const queryClient = new QueryClient();
7
-
8
- const trpcClient = createTRPCClient<AppRouter>({
9
- links: [
10
- httpBatchLink({
11
- url: `${process.env.EXPO_PUBLIC_SERVER_URL}/trpc`,
12
- }),
13
- ],
14
- });
15
-
16
- export const trpc = createTRPCOptionsProxy<AppRouter>({
17
- client: trpcClient,
18
- queryClient,
19
- });