mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
add nuxt and expo with orpc
This commit is contained in:
@@ -1,10 +1,20 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { View, Text, ScrollView } from "react-native";
|
||||
import { Container } from "@/components/container";
|
||||
{{#if (eq api "orpc")}}
|
||||
import { orpc } from "@/utils/orpc";
|
||||
{{/if}}
|
||||
{{#if (eq api "trpc")}}
|
||||
import { trpc } from "@/utils/trpc";
|
||||
{{/if}}
|
||||
|
||||
export default function Home() {
|
||||
{{#if (eq api "orpc")}}
|
||||
const healthCheck = useQuery(orpc.healthCheck.queryOptions());
|
||||
{{/if}}
|
||||
{{#if (eq api "trpc")}}
|
||||
const healthCheck = useQuery(trpc.healthCheck.queryOptions());
|
||||
{{/if}}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
@@ -9,7 +9,12 @@ import {
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
||||
import "../global.css";
|
||||
{{#if (eq api "trpc")}}
|
||||
import { queryClient } from "@/utils/trpc";
|
||||
{{/if}}
|
||||
{{#if (eq api "orpc")}}
|
||||
import { queryClient } from "@/utils/orpc";
|
||||
{{/if}}
|
||||
import { NAV_THEME } from "@/lib/constants";
|
||||
import React, { useRef } from "react";
|
||||
import { useColorScheme } from "@/lib/use-color-scheme";
|
||||
@@ -56,12 +61,12 @@ export default function RootLayout() {
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ThemeProvider value={isDarkColorScheme ? DARK_THEME : LIGHT_THEME}>
|
||||
<StatusBar style={isDarkColorScheme ? "light" : "dark"} />
|
||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||
<GestureHandlerRootView style=\{{ flex: 1 }}>
|
||||
<Stack>
|
||||
<Stack.Screen name="(drawer)" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="(drawer)" options=\{{ headerShown: false }} />
|
||||
<Stack.Screen
|
||||
name="modal"
|
||||
options={{ title: "Modal", presentation: "modal" }}
|
||||
options=\{{ title: "Modal", presentation: "modal" }}
|
||||
/>
|
||||
</Stack>
|
||||
</GestureHandlerRootView>
|
||||
@@ -16,9 +16,6 @@
|
||||
"@react-navigation/native": "^7.0.14",
|
||||
"@tanstack/react-form": "^1.0.5",
|
||||
"@tanstack/react-query": "^5.69.2",
|
||||
"@trpc/client": "^11.0.0",
|
||||
"@trpc/server": "^11.0.0",
|
||||
"@trpc/tanstack-react-query": "^11.0.0",
|
||||
"expo": "^52.0.44",
|
||||
"expo-constants": "~17.0.8",
|
||||
"expo-linking": "~7.0.5",
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import type { AppRouter } from "../../server/src/routers";
|
||||
import { QueryClient } from "@tanstack/react-query";
|
||||
import { createTRPCClient, httpBatchLink } from "@trpc/client";
|
||||
import { createTRPCOptionsProxy } from "@trpc/tanstack-react-query";
|
||||
|
||||
export const queryClient = new QueryClient();
|
||||
|
||||
const trpcClient = createTRPCClient<AppRouter>({
|
||||
links: [
|
||||
httpBatchLink({
|
||||
url: `${process.env.EXPO_PUBLIC_SERVER_URL}/trpc`,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export const trpc = createTRPCOptionsProxy<AppRouter>({
|
||||
client: trpcClient,
|
||||
queryClient,
|
||||
});
|
||||
24
apps/cli/templates/frontend/nuxt/_gitignore
Normal file
24
apps/cli/templates/frontend/nuxt/_gitignore
Normal file
@@ -0,0 +1,24 @@
|
||||
# Nuxt dev/build outputs
|
||||
.output
|
||||
.data
|
||||
.nuxt
|
||||
.nitro
|
||||
.cache
|
||||
dist
|
||||
|
||||
# Node dependencies
|
||||
node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.fleet
|
||||
.idea
|
||||
|
||||
# Local env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
15
apps/cli/templates/frontend/nuxt/app/app.config.ts
Normal file
15
apps/cli/templates/frontend/nuxt/app/app.config.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export default defineAppConfig({
|
||||
// https://ui.nuxt.com/getting-started/theme#design-system
|
||||
ui: {
|
||||
colors: {
|
||||
primary: 'emerald',
|
||||
neutral: 'slate',
|
||||
},
|
||||
button: {
|
||||
defaultVariants: {
|
||||
// Set default button color to neutral
|
||||
// color: 'neutral'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
13
apps/cli/templates/frontend/nuxt/app/app.vue
Normal file
13
apps/cli/templates/frontend/nuxt/app/app.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { VueQueryDevtools } from '@tanstack/vue-query-devtools'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLoadingIndicator />
|
||||
<UApp>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</UApp>
|
||||
<VueQueryDevtools />
|
||||
</template>
|
||||
2
apps/cli/templates/frontend/nuxt/app/assets/css/main.css
Normal file
2
apps/cli/templates/frontend/nuxt/app/assets/css/main.css
Normal file
@@ -0,0 +1,2 @@
|
||||
@import "tailwindcss";
|
||||
@import "@nuxt/ui";
|
||||
@@ -0,0 +1,45 @@
|
||||
<script setup lang="ts">
|
||||
import { USeparator } from '#components';
|
||||
import ModeToggle from './ModeToggle.vue'
|
||||
{{#if auth}}
|
||||
import UserMenu from './UserMenu.vue'
|
||||
{{/if}}
|
||||
|
||||
const links = [
|
||||
{ to: "/", label: "Home" },
|
||||
{{#if auth}}
|
||||
{ to: "/dashboard", label: "Dashboard" },
|
||||
{{/if}}
|
||||
{{#if (includes examples "todo")}}
|
||||
{ to: "/todos", label: "Todos" },
|
||||
{{/if}}
|
||||
{{#if (includes examples "ai")}}
|
||||
{ to: "/ai", label: "AI Chat" },
|
||||
{{/if}}
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex flex-row items-center justify-between px-2 py-1">
|
||||
<nav class="flex gap-4 text-lg">
|
||||
<NuxtLink
|
||||
v-for="link in links"
|
||||
:key="link.to"
|
||||
:to="link.to"
|
||||
class="text-foreground hover:text-primary"
|
||||
active-class="text-primary font-semibold"
|
||||
>
|
||||
\{{ link.label }}
|
||||
</NuxtLink>
|
||||
</nav>
|
||||
<div class="flex items-center gap-2">
|
||||
<ModeToggle />
|
||||
{{#if auth}}
|
||||
<UserMenu />
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<USeparator />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="flex h-full items-center justify-center pt-8">
|
||||
<UIcon name="i-lucide-loader-2" class="animate-spin text-2xl" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
const colorMode = useColorMode()
|
||||
|
||||
const isDark = computed({
|
||||
get () {
|
||||
return colorMode.value === 'dark'
|
||||
},
|
||||
set (value) {
|
||||
colorMode.preference = value ? 'dark' : 'light'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center">
|
||||
<USwitch
|
||||
v-model="isDark"
|
||||
:checked-icon="isDark ? 'i-lucide-moon' : ''"
|
||||
:unchecked-icon="!isDark ? 'i-lucide-sun' : ''"
|
||||
class="mr-2"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
11
apps/cli/templates/frontend/nuxt/app/layouts/default.vue
Normal file
11
apps/cli/templates/frontend/nuxt/app/layouts/default.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="grid grid-rows-[auto_1fr] h-full">
|
||||
<Header />
|
||||
<main class="overflow-y-auto">
|
||||
<slot />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
63
apps/cli/templates/frontend/nuxt/app/pages/index.vue
Normal file
63
apps/cli/templates/frontend/nuxt/app/pages/index.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<script setup lang="ts">
|
||||
const { $orpc } = useNuxtApp()
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
|
||||
const TITLE_TEXT = `
|
||||
██████╗ ███████╗████████╗████████╗███████╗██████╗
|
||||
██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗
|
||||
██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝
|
||||
██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗
|
||||
██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║
|
||||
╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
|
||||
|
||||
████████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗
|
||||
╚══██╔══╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
|
||||
██║ ███████╗ ██║ ███████║██║ █████╔╝
|
||||
██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗
|
||||
██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗
|
||||
╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
|
||||
`;
|
||||
|
||||
const healthCheck = useQuery($orpc.healthCheck.queryOptions())
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container mx-auto max-w-3xl px-4 py-2">
|
||||
<pre class="overflow-x-auto font-mono text-sm whitespace-pre-wrap">{{ TITLE_TEXT }}</pre>
|
||||
<div class="grid gap-6 mt-4">
|
||||
<section class="rounded-lg border p-4">
|
||||
<h2 class="mb-2 font-medium">API Status</h2>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<div
|
||||
class="w-2 h-2 rounded-full"
|
||||
:class="{
|
||||
'bg-yellow-500 animate-pulse': healthCheck.status.value === 'pending',
|
||||
'bg-green-500': healthCheck.status.value === 'success',
|
||||
'bg-red-500': healthCheck.status.value === 'error',
|
||||
'bg-gray-400': healthCheck.status.value !== 'pending' &&
|
||||
healthCheck.status.value !== 'success' &&
|
||||
healthCheck.status.value !== 'error'
|
||||
}"
|
||||
></div>
|
||||
<span class="text-sm text-muted-foreground">
|
||||
<template v-if="healthCheck.status.value === 'pending'">
|
||||
Checking...
|
||||
</template>
|
||||
<template v-else-if="healthCheck.status.value === 'success'">
|
||||
Connected ({{ healthCheck.data.value }})
|
||||
</template>
|
||||
<template v-else-if="healthCheck.status.value === 'error'">
|
||||
Error: {{ healthCheck.error.value?.message || 'Failed to connect' }}
|
||||
</template>
|
||||
<template v-else>
|
||||
Idle
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
44
apps/cli/templates/frontend/nuxt/app/plugins/vue-query.ts
Normal file
44
apps/cli/templates/frontend/nuxt/app/plugins/vue-query.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import type {
|
||||
DehydratedState,
|
||||
VueQueryPluginOptions,
|
||||
} from '@tanstack/vue-query'
|
||||
import {
|
||||
dehydrate,
|
||||
hydrate,
|
||||
QueryCache,
|
||||
QueryClient,
|
||||
VueQueryPlugin,
|
||||
} from '@tanstack/vue-query'
|
||||
|
||||
export default defineNuxtPlugin((nuxt) => {
|
||||
const vueQueryState = useState<DehydratedState | null>('vue-query')
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
queryCache: new QueryCache({
|
||||
onError: (error) => {
|
||||
console.log(error)
|
||||
toast.add({
|
||||
title: 'Error',
|
||||
description: error?.message || 'An unexpected error occurred.',
|
||||
})
|
||||
},
|
||||
}),
|
||||
})
|
||||
const options: VueQueryPluginOptions = { queryClient }
|
||||
|
||||
nuxt.vueApp.use(VueQueryPlugin, options)
|
||||
|
||||
if (import.meta.server) {
|
||||
nuxt.hooks.hook('app:rendered', () => {
|
||||
vueQueryState.value = dehydrate(queryClient)
|
||||
})
|
||||
}
|
||||
|
||||
if (import.meta.client) {
|
||||
nuxt.hooks.hook('app:created', () => {
|
||||
hydrate(queryClient, vueQueryState.value)
|
||||
})
|
||||
}
|
||||
})
|
||||
19
apps/cli/templates/frontend/nuxt/nuxt.config.ts.hbs
Normal file
19
apps/cli/templates/frontend/nuxt/nuxt.config.ts.hbs
Normal file
@@ -0,0 +1,19 @@
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
export default defineNuxtConfig({
|
||||
compatibilityDate: '2024-11-01',
|
||||
future: {
|
||||
compatibilityVersion: 4
|
||||
},
|
||||
devtools: { enabled: true },
|
||||
modules: ['@nuxt/ui'],
|
||||
css: ['~/assets/css/main.css'],
|
||||
devServer: {
|
||||
port: 3001
|
||||
},
|
||||
ssr: false,
|
||||
runtimeConfig: {
|
||||
public: {
|
||||
serverURL: process.env.NUXT_PUBLIC_SERVER_URL,
|
||||
}
|
||||
}
|
||||
})
|
||||
25
apps/cli/templates/frontend/nuxt/package.json
Normal file
25
apps/cli/templates/frontend/nuxt/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "web",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxt/ui": "3.0.2",
|
||||
"@tanstack/vue-query": "^5.74.5",
|
||||
"nuxt": "^3.16.2",
|
||||
"typescript": "^5.6.3",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0",
|
||||
"zod": "^3.24.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tanstack/vue-query-devtools": "^5.74.5",
|
||||
"@iconify-json/lucide": "^1.2.38"
|
||||
}
|
||||
}
|
||||
BIN
apps/cli/templates/frontend/nuxt/public/favicon.ico
Normal file
BIN
apps/cli/templates/frontend/nuxt/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
2
apps/cli/templates/frontend/nuxt/public/robots.txt
Normal file
2
apps/cli/templates/frontend/nuxt/public/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
User-Agent: *
|
||||
Disallow:
|
||||
3
apps/cli/templates/frontend/nuxt/server/tsconfig.json
Normal file
3
apps/cli/templates/frontend/nuxt/server/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../.nuxt/tsconfig.server.json"
|
||||
}
|
||||
4
apps/cli/templates/frontend/nuxt/tsconfig.json
Normal file
4
apps/cli/templates/frontend/nuxt/tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
// https://nuxt.com/docs/guide/concepts/typescript
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
}
|
||||
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Reference in New Issue
Block a user