create-better-t-stack 2.7.2 → 2.8.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.
- package/dist/index.js +11 -11
- package/package.json +1 -1
- package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +30 -0
- package/templates/auth/web/solid/src/components/sign-in-form.tsx +132 -0
- package/templates/auth/web/solid/src/components/sign-up-form.tsx +158 -0
- package/templates/auth/web/solid/src/components/user-menu.tsx +54 -0
- package/templates/auth/web/solid/src/lib/auth-client.ts +5 -0
- package/templates/auth/web/solid/src/routes/dashboard.tsx +38 -0
- package/templates/auth/web/solid/src/routes/login.tsx +23 -0
- package/templates/db/prisma/sqlite/prisma/schema/schema.prisma +1 -1
- package/templates/examples/todo/web/solid/src/routes/todos.tsx +132 -0
- package/templates/frontend/react/react-router/vite.config.ts.hbs +3 -7
- package/templates/frontend/react/tanstack-router/vite.config.ts.hbs +3 -5
- package/templates/frontend/solid/_gitignore +7 -0
- package/templates/frontend/solid/index.html +13 -0
- package/templates/frontend/solid/package.json +33 -0
- package/templates/frontend/solid/public/robots.txt +3 -0
- package/templates/frontend/solid/src/components/header.tsx.hbs +38 -0
- package/templates/frontend/solid/src/components/loader.tsx +9 -0
- package/templates/frontend/solid/src/main.tsx.hbs +32 -0
- package/templates/frontend/solid/src/routes/__root.tsx.hbs +21 -0
- package/templates/frontend/solid/src/routes/index.tsx.hbs +65 -0
- package/templates/frontend/solid/src/routes/todos.tsx +132 -0
- package/templates/frontend/solid/src/styles.css +5 -0
- package/templates/frontend/solid/tsconfig.json +29 -0
- package/templates/frontend/solid/vite.config.js.hbs +39 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createFileRoute } from "@tanstack/solid-router";
|
|
2
|
+
import { useQuery } from "@tanstack/solid-query";
|
|
3
|
+
import { orpc } from "../utils/orpc";
|
|
4
|
+
import { Match, Switch } from "solid-js";
|
|
5
|
+
|
|
6
|
+
export const Route = createFileRoute("/")({
|
|
7
|
+
component: App,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const TITLE_TEXT = `
|
|
11
|
+
██████╗ ███████╗████████╗████████╗███████╗██████╗
|
|
12
|
+
██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗
|
|
13
|
+
██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝
|
|
14
|
+
██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗
|
|
15
|
+
██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║
|
|
16
|
+
╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
|
|
17
|
+
|
|
18
|
+
████████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗
|
|
19
|
+
╚══██╔══╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
|
|
20
|
+
██║ ███████╗ ██║ ███████║██║ █████╔╝
|
|
21
|
+
██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗
|
|
22
|
+
██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗
|
|
23
|
+
╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
function App() {
|
|
27
|
+
const healthCheck = useQuery(() => orpc.healthCheck.queryOptions());
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div class="container mx-auto max-w-3xl px-4 py-2">
|
|
31
|
+
<pre class="overflow-x-auto font-mono text-sm">{TITLE_TEXT}</pre>
|
|
32
|
+
<div class="grid gap-6">
|
|
33
|
+
<section class="rounded-lg border p-4">
|
|
34
|
+
<h2 class="mb-2 font-medium">API Status</h2>
|
|
35
|
+
<Switch>
|
|
36
|
+
<Match when={healthCheck.isPending}>
|
|
37
|
+
<div class="flex items-center gap-2">
|
|
38
|
+
<div class="h-2 w-2 rounded-full bg-gray-500 animate-pulse" />{" "}
|
|
39
|
+
<span class="text-sm text-muted-foreground">Checking...</span>
|
|
40
|
+
</div>
|
|
41
|
+
</Match>
|
|
42
|
+
<Match when={healthCheck.isError}>
|
|
43
|
+
<div class="flex items-center gap-2">
|
|
44
|
+
<div class="h-2 w-2 rounded-full bg-red-500" />
|
|
45
|
+
<span class="text-sm text-muted-foreground">Disconnected</span>
|
|
46
|
+
</div>
|
|
47
|
+
</Match>
|
|
48
|
+
<Match when={healthCheck.isSuccess}>
|
|
49
|
+
<div class="flex items-center gap-2">
|
|
50
|
+
<div
|
|
51
|
+
class={`h-2 w-2 rounded-full ${healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
|
|
52
|
+
/>
|
|
53
|
+
<span class="text-sm text-muted-foreground">
|
|
54
|
+
{healthCheck.data
|
|
55
|
+
? "Connected"
|
|
56
|
+
: "Disconnected (Success but no data)"}
|
|
57
|
+
</span>
|
|
58
|
+
</div>
|
|
59
|
+
</Match>
|
|
60
|
+
</Switch>
|
|
61
|
+
</section>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { createFileRoute } from "@tanstack/solid-router";
|
|
2
|
+
import { Loader2, Trash2 } from "lucide-solid";
|
|
3
|
+
import { createSignal, For, Show } from "solid-js";
|
|
4
|
+
import { orpc } from "@/utils/orpc";
|
|
5
|
+
import { useQuery, useMutation } from "@tanstack/solid-query";
|
|
6
|
+
|
|
7
|
+
export const Route = createFileRoute("/todos")({
|
|
8
|
+
component: TodosRoute,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
function TodosRoute() {
|
|
12
|
+
const [newTodoText, setNewTodoText] = createSignal("");
|
|
13
|
+
|
|
14
|
+
const todos = useQuery(() => orpc.todo.getAll.queryOptions());
|
|
15
|
+
|
|
16
|
+
const createMutation = useMutation(() =>
|
|
17
|
+
orpc.todo.create.mutationOptions({
|
|
18
|
+
onSuccess: () => {
|
|
19
|
+
todos.refetch();
|
|
20
|
+
setNewTodoText("");
|
|
21
|
+
},
|
|
22
|
+
}),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const toggleMutation = useMutation(() =>
|
|
26
|
+
orpc.todo.toggle.mutationOptions({
|
|
27
|
+
onSuccess: () => todos.refetch(),
|
|
28
|
+
}),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const deleteMutation = useMutation(() =>
|
|
32
|
+
orpc.todo.delete.mutationOptions({
|
|
33
|
+
onSuccess: () => todos.refetch(),
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const handleAddTodo = (e: Event) => {
|
|
38
|
+
e.preventDefault();
|
|
39
|
+
if (newTodoText().trim()) {
|
|
40
|
+
createMutation.mutate({ text: newTodoText() });
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handleToggleTodo = (id: number, completed: boolean) => {
|
|
45
|
+
toggleMutation.mutate({ id, completed: !completed });
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const handleDeleteTodo = (id: number) => {
|
|
49
|
+
deleteMutation.mutate({ id });
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div class="mx-auto w-full max-w-md py-10">
|
|
54
|
+
<div class="rounded-lg border p-6 shadow-sm">
|
|
55
|
+
<div class="mb-4">
|
|
56
|
+
<h2 class="text-xl font-semibold">Todo List</h2>
|
|
57
|
+
<p class="text-sm">Manage your tasks efficiently</p>
|
|
58
|
+
</div>
|
|
59
|
+
<div>
|
|
60
|
+
<form
|
|
61
|
+
onSubmit={handleAddTodo}
|
|
62
|
+
class="mb-6 flex items-center space-x-2"
|
|
63
|
+
>
|
|
64
|
+
<input
|
|
65
|
+
type="text"
|
|
66
|
+
value={newTodoText()}
|
|
67
|
+
onInput={(e) => setNewTodoText(e.currentTarget.value)}
|
|
68
|
+
placeholder="Add a new task..."
|
|
69
|
+
disabled={createMutation.isPending}
|
|
70
|
+
class="w-full rounded-md border p-2 text-sm"
|
|
71
|
+
/>
|
|
72
|
+
<button
|
|
73
|
+
type="submit"
|
|
74
|
+
disabled={createMutation.isPending || !newTodoText().trim()}
|
|
75
|
+
class="rounded-md bg-blue-600 px-4 py-2 text-sm text-white disabled:opacity-50"
|
|
76
|
+
>
|
|
77
|
+
<Show when={createMutation.isPending} fallback="Add">
|
|
78
|
+
<Loader2 class="h-4 w-4 animate-spin" />
|
|
79
|
+
</Show>
|
|
80
|
+
</button>
|
|
81
|
+
</form>
|
|
82
|
+
|
|
83
|
+
<Show when={todos.isLoading}>
|
|
84
|
+
<div class="flex justify-center py-4">
|
|
85
|
+
<Loader2 class="h-6 w-6 animate-spin" />
|
|
86
|
+
</div>
|
|
87
|
+
</Show>
|
|
88
|
+
|
|
89
|
+
<Show when={!todos.isLoading && todos.data?.length === 0}>
|
|
90
|
+
<p class="py-4 text-center">No todos yet. Add one above!</p>
|
|
91
|
+
</Show>
|
|
92
|
+
|
|
93
|
+
<Show when={!todos.isLoading}>
|
|
94
|
+
<ul class="space-y-2">
|
|
95
|
+
<For each={todos.data}>
|
|
96
|
+
{(todo) => (
|
|
97
|
+
<li class="flex items-center justify-between rounded-md border p-2">
|
|
98
|
+
<div class="flex items-center space-x-2">
|
|
99
|
+
<input
|
|
100
|
+
type="checkbox"
|
|
101
|
+
checked={todo.completed}
|
|
102
|
+
onChange={() =>
|
|
103
|
+
handleToggleTodo(todo.id, todo.completed)
|
|
104
|
+
}
|
|
105
|
+
id={`todo-${todo.id}`}
|
|
106
|
+
class="h-4 w-4"
|
|
107
|
+
/>
|
|
108
|
+
<label
|
|
109
|
+
for={`todo-${todo.id}`}
|
|
110
|
+
class={todo.completed ? "line-through" : ""}
|
|
111
|
+
>
|
|
112
|
+
{todo.text}
|
|
113
|
+
</label>
|
|
114
|
+
</div>
|
|
115
|
+
<button
|
|
116
|
+
type="button"
|
|
117
|
+
onClick={() => handleDeleteTodo(todo.id)}
|
|
118
|
+
aria-label="Delete todo"
|
|
119
|
+
class="ml-2 rounded-md p-1"
|
|
120
|
+
>
|
|
121
|
+
<Trash2 class="h-4 w-4" />
|
|
122
|
+
</button>
|
|
123
|
+
</li>
|
|
124
|
+
)}
|
|
125
|
+
</For>
|
|
126
|
+
</ul>
|
|
127
|
+
</Show>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"include": ["**/*.ts", "**/*.tsx"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"jsx": "preserve",
|
|
6
|
+
"jsxImportSource": "solid-js",
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
9
|
+
"types": ["vite/client"],
|
|
10
|
+
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"strict": true,
|
|
18
|
+
"noUnusedLocals": true,
|
|
19
|
+
"noUnusedParameters": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
"noUncheckedSideEffectImports": true,
|
|
22
|
+
|
|
23
|
+
"rootDirs": ["."],
|
|
24
|
+
"baseUrl": ".",
|
|
25
|
+
"paths": {
|
|
26
|
+
"@/*": ["./src/*"]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { defineConfig } from "vite";
|
|
2
|
+
import { TanStackRouterVite } from "@tanstack/router-plugin/vite";
|
|
3
|
+
import solidPlugin from "vite-plugin-solid";
|
|
4
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
{{#if (includes addons "pwa")}}
|
|
7
|
+
import { VitePWA } from "vite-plugin-pwa";
|
|
8
|
+
{{/if}}
|
|
9
|
+
|
|
10
|
+
export default defineConfig({
|
|
11
|
+
plugins: [
|
|
12
|
+
TanStackRouterVite({ target: "solid", autoCodeSplitting: true }),
|
|
13
|
+
solidPlugin(),
|
|
14
|
+
tailwindcss(),
|
|
15
|
+
{{#if (includes addons "pwa")}}
|
|
16
|
+
VitePWA({
|
|
17
|
+
registerType: "autoUpdate",
|
|
18
|
+
manifest: {
|
|
19
|
+
name: "{{projectName}}",
|
|
20
|
+
short_name: "{{projectName}}",
|
|
21
|
+
description: "{{projectName}} - PWA Application",
|
|
22
|
+
theme_color: "#0c0c0c",
|
|
23
|
+
},
|
|
24
|
+
pwaAssets: {
|
|
25
|
+
disabled: false,
|
|
26
|
+
config: true,
|
|
27
|
+
},
|
|
28
|
+
devOptions: {
|
|
29
|
+
enabled: true,
|
|
30
|
+
},
|
|
31
|
+
}),
|
|
32
|
+
{{/if}}
|
|
33
|
+
],
|
|
34
|
+
resolve: {
|
|
35
|
+
alias: {
|
|
36
|
+
"@": path.resolve(__dirname, "./src"),
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
});
|