@newt-app/templates 0.2.0 → 0.2.2
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 +445 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1909,6 +1909,443 @@ export function Logo({ className }: LogoProps) {
|
|
|
1909
1909
|
}`
|
|
1910
1910
|
};
|
|
1911
1911
|
|
|
1912
|
+
// src/shadcn-ui/templates/layout.ts
|
|
1913
|
+
var layout_default2 = {
|
|
1914
|
+
filename: "apps/web/app/layout.tsx",
|
|
1915
|
+
template: `import type { Metadata } from "next";
|
|
1916
|
+
import localFont from "next/font/local";
|
|
1917
|
+
import Providers from "@/app/providers";
|
|
1918
|
+
import "@<%= projectName %>/ui/globals.css";
|
|
1919
|
+
|
|
1920
|
+
const geistSans = localFont({
|
|
1921
|
+
src: "./fonts/GeistVF.woff",
|
|
1922
|
+
variable: "--font-geist-sans",
|
|
1923
|
+
});
|
|
1924
|
+
const geistMono = localFont({
|
|
1925
|
+
src: "./fonts/GeistMonoVF.woff",
|
|
1926
|
+
variable: "--font-geist-mono",
|
|
1927
|
+
});
|
|
1928
|
+
|
|
1929
|
+
export const metadata: Metadata = {
|
|
1930
|
+
title: "<%= projectName %>",
|
|
1931
|
+
description: "Next + Nest = Newt",
|
|
1932
|
+
icons: {
|
|
1933
|
+
icon: [
|
|
1934
|
+
{ url: "/icon0.svg", type: "image/svg+xml" },
|
|
1935
|
+
{ url: "/icon1.png", type: "image/png" },
|
|
1936
|
+
],
|
|
1937
|
+
apple: "/apple-icon.png",
|
|
1938
|
+
},
|
|
1939
|
+
};
|
|
1940
|
+
|
|
1941
|
+
export default function RootLayout({
|
|
1942
|
+
children,
|
|
1943
|
+
}: Readonly<{
|
|
1944
|
+
children: React.ReactNode;
|
|
1945
|
+
}>) {
|
|
1946
|
+
return (
|
|
1947
|
+
<html lang="en" suppressHydrationWarning>
|
|
1948
|
+
<body className={\`\${geistSans.variable} \${geistMono.variable} h-full\`}>
|
|
1949
|
+
<Providers>{children}</Providers>
|
|
1950
|
+
</body>
|
|
1951
|
+
</html>
|
|
1952
|
+
);
|
|
1953
|
+
}`
|
|
1954
|
+
};
|
|
1955
|
+
|
|
1956
|
+
// src/shadcn-ui/templates/page.ts
|
|
1957
|
+
var page_default2 = {
|
|
1958
|
+
filename: "apps/web/app/page.tsx",
|
|
1959
|
+
template: `'use client';
|
|
1960
|
+
|
|
1961
|
+
import { useQuery } from '@tanstack/react-query';
|
|
1962
|
+
import { authClient } from '@/lib/auth-client';
|
|
1963
|
+
import { AuthForm } from '@/app/auth-form';
|
|
1964
|
+
import { Link } from '@<%= projectName %>/ui/link';
|
|
1965
|
+
import { Logo } from '@<%= projectName %>/ui/logo';
|
|
1966
|
+
import { TodoList } from '@/app/todo-list';
|
|
1967
|
+
import { Card, CardContent, CardHeader, CardTitle } from '@<%= projectName %>/ui/card';
|
|
1968
|
+
|
|
1969
|
+
export default function Home() {
|
|
1970
|
+
const { data: session, isPending } = authClient.useSession();
|
|
1971
|
+
|
|
1972
|
+
const { data: hello } = useQuery({
|
|
1973
|
+
queryKey: ['hello'],
|
|
1974
|
+
queryFn: () => fetch('/api/hello').then((r) => r.json()),
|
|
1975
|
+
});
|
|
1976
|
+
|
|
1977
|
+
return (
|
|
1978
|
+
<main className="max-w-lg mx-auto min-h-full px-4 py-8 space-y-4">
|
|
1979
|
+
<div className="pb-4 border-b">
|
|
1980
|
+
<p className="font-mono text-xs text-muted-foreground">apps/web/page.tsx</p>
|
|
1981
|
+
<p className="text-sm text-muted-foreground">Delete me to get started!</p>
|
|
1982
|
+
</div>
|
|
1983
|
+
|
|
1984
|
+
<div className="flex items-center gap-3 py-2">
|
|
1985
|
+
<Logo className="w-10 h-auto text-foreground" />
|
|
1986
|
+
<div>
|
|
1987
|
+
<h1 className="text-4xl font-black tracking-tight"><%= projectName %></h1>
|
|
1988
|
+
<p className="text-sm text-muted-foreground tracking-widest uppercase">
|
|
1989
|
+
Next + Nest = Newt \u{1F49C}
|
|
1990
|
+
</p>
|
|
1991
|
+
</div>
|
|
1992
|
+
</div>
|
|
1993
|
+
|
|
1994
|
+
<Card>
|
|
1995
|
+
<CardHeader>
|
|
1996
|
+
<CardTitle className="text-xs font-mono uppercase tracking-widest text-muted-foreground">
|
|
1997
|
+
next.js
|
|
1998
|
+
</CardTitle>
|
|
1999
|
+
</CardHeader>
|
|
2000
|
+
<CardContent>
|
|
2001
|
+
<p className="font-mono text-sm">apps/web/layout.tsx</p>
|
|
2002
|
+
<p className="text-sm text-muted-foreground">Next.js rendering</p>
|
|
2003
|
+
</CardContent>
|
|
2004
|
+
</Card>
|
|
2005
|
+
|
|
2006
|
+
<Card>
|
|
2007
|
+
<CardHeader>
|
|
2008
|
+
<CardTitle className="text-xs font-mono uppercase tracking-widest text-muted-foreground">
|
|
2009
|
+
nest.js
|
|
2010
|
+
</CardTitle>
|
|
2011
|
+
</CardHeader>
|
|
2012
|
+
<CardContent>
|
|
2013
|
+
<p className="font-mono text-sm">GET /api/hello</p>
|
|
2014
|
+
<pre className="mt-2 rounded-md border p-3 text-sm bg-muted/50">
|
|
2015
|
+
<code>{JSON.stringify(hello, null, 2)}</code>
|
|
2016
|
+
</pre>
|
|
2017
|
+
</CardContent>
|
|
2018
|
+
</Card>
|
|
2019
|
+
|
|
2020
|
+
<Card>
|
|
2021
|
+
<CardHeader>
|
|
2022
|
+
<CardTitle className="text-xs font-mono uppercase tracking-widest text-muted-foreground">
|
|
2023
|
+
better-auth
|
|
2024
|
+
</CardTitle>
|
|
2025
|
+
</CardHeader>
|
|
2026
|
+
<CardContent>
|
|
2027
|
+
{isPending ? (
|
|
2028
|
+
<p className="text-sm text-muted-foreground">Loading\u2026</p>
|
|
2029
|
+
) : session ? (
|
|
2030
|
+
<TodoList session={session} />
|
|
2031
|
+
) : (
|
|
2032
|
+
<AuthForm />
|
|
2033
|
+
)}
|
|
2034
|
+
</CardContent>
|
|
2035
|
+
</Card>
|
|
2036
|
+
|
|
2037
|
+
<div className="px-2 py-4 text-sm">
|
|
2038
|
+
<p className="mb-2 text-muted-foreground">Learn more</p>
|
|
2039
|
+
<ul className="list-disc list-inside space-y-2 mt-2">
|
|
2040
|
+
<li>
|
|
2041
|
+
<Link href="https://github.com">GitHub</Link>
|
|
2042
|
+
</li>
|
|
2043
|
+
<li>
|
|
2044
|
+
<Link href="https://nextjs.org">Next.js</Link>
|
|
2045
|
+
</li>
|
|
2046
|
+
<li>
|
|
2047
|
+
<Link href="https://nestjs.com">NestJS</Link>
|
|
2048
|
+
</li>
|
|
2049
|
+
</ul>
|
|
2050
|
+
</div>
|
|
2051
|
+
</main>
|
|
2052
|
+
);
|
|
2053
|
+
}`
|
|
2054
|
+
};
|
|
2055
|
+
|
|
2056
|
+
// src/shadcn-ui/templates/providers.ts
|
|
2057
|
+
var providers_default2 = {
|
|
2058
|
+
filename: "apps/web/app/providers.tsx",
|
|
2059
|
+
template: `'use client';
|
|
2060
|
+
|
|
2061
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
2062
|
+
import { ThemeProvider } from 'next-themes';
|
|
2063
|
+
import { useState } from 'react';
|
|
2064
|
+
|
|
2065
|
+
export default function Providers({ children }: { children: React.ReactNode }) {
|
|
2066
|
+
const [client] = useState(() => new QueryClient());
|
|
2067
|
+
return (
|
|
2068
|
+
<QueryClientProvider client={client}>
|
|
2069
|
+
<ThemeProvider attribute="class" defaultTheme="dark" disableTransitionOnChange>
|
|
2070
|
+
{children}
|
|
2071
|
+
</ThemeProvider>
|
|
2072
|
+
</QueryClientProvider>
|
|
2073
|
+
);
|
|
2074
|
+
}`
|
|
2075
|
+
};
|
|
2076
|
+
|
|
2077
|
+
// src/shadcn-ui/templates/auth-form.ts
|
|
2078
|
+
var auth_form_default2 = {
|
|
2079
|
+
filename: "apps/web/app/auth-form.tsx",
|
|
2080
|
+
template: `'use client';
|
|
2081
|
+
|
|
2082
|
+
import { useForm } from '@tanstack/react-form';
|
|
2083
|
+
import { useState } from 'react';
|
|
2084
|
+
import { authClient } from '@/lib/auth-client';
|
|
2085
|
+
import { Button } from '@<%= projectName %>/ui/button';
|
|
2086
|
+
import { Input } from '@<%= projectName %>/ui/input';
|
|
2087
|
+
import { Label } from '@<%= projectName %>/ui/label';
|
|
2088
|
+
|
|
2089
|
+
export function AuthForm() {
|
|
2090
|
+
const [tab, setTab] = useState<'signin' | 'signup'>('signup');
|
|
2091
|
+
const [error, setError] = useState('');
|
|
2092
|
+
|
|
2093
|
+
const form = useForm({
|
|
2094
|
+
defaultValues: { name: '', email: '', password: '' },
|
|
2095
|
+
onSubmit: async ({ value }) => {
|
|
2096
|
+
setError('');
|
|
2097
|
+
if (tab === 'signin') {
|
|
2098
|
+
const { error } = await authClient.signIn.email(value);
|
|
2099
|
+
if (error) setError(error.message ?? 'Sign in failed');
|
|
2100
|
+
} else {
|
|
2101
|
+
const { error } = await authClient.signUp.email(value);
|
|
2102
|
+
if (error) setError(error.message ?? 'Sign up failed');
|
|
2103
|
+
}
|
|
2104
|
+
},
|
|
2105
|
+
});
|
|
2106
|
+
|
|
2107
|
+
return (
|
|
2108
|
+
<>
|
|
2109
|
+
<div className="flex gap-1 mb-6">
|
|
2110
|
+
<Button
|
|
2111
|
+
type="button"
|
|
2112
|
+
variant={tab === 'signup' ? 'secondary' : 'ghost'}
|
|
2113
|
+
size="sm"
|
|
2114
|
+
onClick={() => setTab('signup')}
|
|
2115
|
+
>
|
|
2116
|
+
Sign up
|
|
2117
|
+
</Button>
|
|
2118
|
+
<Button
|
|
2119
|
+
type="button"
|
|
2120
|
+
variant={tab === 'signin' ? 'secondary' : 'ghost'}
|
|
2121
|
+
size="sm"
|
|
2122
|
+
onClick={() => setTab('signin')}
|
|
2123
|
+
>
|
|
2124
|
+
Sign in
|
|
2125
|
+
</Button>
|
|
2126
|
+
</div>
|
|
2127
|
+
|
|
2128
|
+
<form
|
|
2129
|
+
onSubmit={(e) => {
|
|
2130
|
+
e.preventDefault();
|
|
2131
|
+
form.handleSubmit();
|
|
2132
|
+
}}
|
|
2133
|
+
className="space-y-4"
|
|
2134
|
+
>
|
|
2135
|
+
{tab === 'signup' && (
|
|
2136
|
+
<form.Field name="name">
|
|
2137
|
+
{(field) => (
|
|
2138
|
+
<div className="space-y-1.5">
|
|
2139
|
+
<Label htmlFor="name">Name</Label>
|
|
2140
|
+
<Input
|
|
2141
|
+
id="name"
|
|
2142
|
+
type="text"
|
|
2143
|
+
value={field.state.value}
|
|
2144
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
2145
|
+
required
|
|
2146
|
+
/>
|
|
2147
|
+
</div>
|
|
2148
|
+
)}
|
|
2149
|
+
</form.Field>
|
|
2150
|
+
)}
|
|
2151
|
+
|
|
2152
|
+
<form.Field name="email">
|
|
2153
|
+
{(field) => (
|
|
2154
|
+
<div className="space-y-1.5">
|
|
2155
|
+
<Label htmlFor="email">Email</Label>
|
|
2156
|
+
<Input
|
|
2157
|
+
id="email"
|
|
2158
|
+
type="email"
|
|
2159
|
+
value={field.state.value}
|
|
2160
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
2161
|
+
required
|
|
2162
|
+
/>
|
|
2163
|
+
</div>
|
|
2164
|
+
)}
|
|
2165
|
+
</form.Field>
|
|
2166
|
+
|
|
2167
|
+
<form.Field name="password">
|
|
2168
|
+
{(field) => (
|
|
2169
|
+
<div className="space-y-1.5">
|
|
2170
|
+
<Label htmlFor="password">Password</Label>
|
|
2171
|
+
<Input
|
|
2172
|
+
id="password"
|
|
2173
|
+
type="password"
|
|
2174
|
+
value={field.state.value}
|
|
2175
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
2176
|
+
required
|
|
2177
|
+
minLength={8}
|
|
2178
|
+
/>
|
|
2179
|
+
</div>
|
|
2180
|
+
)}
|
|
2181
|
+
</form.Field>
|
|
2182
|
+
|
|
2183
|
+
{error && <p className="text-sm text-destructive">{error}</p>}
|
|
2184
|
+
|
|
2185
|
+
<form.Subscribe selector={(s) => s.isSubmitting}>
|
|
2186
|
+
{(isSubmitting) => (
|
|
2187
|
+
<Button type="submit" disabled={isSubmitting} className="w-full">
|
|
2188
|
+
{isSubmitting
|
|
2189
|
+
? 'Loading\u2026'
|
|
2190
|
+
: tab === 'signin'
|
|
2191
|
+
? 'Sign in'
|
|
2192
|
+
: 'Create account'}
|
|
2193
|
+
</Button>
|
|
2194
|
+
)}
|
|
2195
|
+
</form.Subscribe>
|
|
2196
|
+
</form>
|
|
2197
|
+
</>
|
|
2198
|
+
);
|
|
2199
|
+
}`
|
|
2200
|
+
};
|
|
2201
|
+
|
|
2202
|
+
// src/shadcn-ui/templates/todo-list.ts
|
|
2203
|
+
var todo_list_default2 = {
|
|
2204
|
+
filename: "apps/web/app/todo-list.tsx",
|
|
2205
|
+
template: `'use client';
|
|
2206
|
+
|
|
2207
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
2208
|
+
import { useForm } from '@tanstack/react-form';
|
|
2209
|
+
import { authClient } from '@/lib/auth-client';
|
|
2210
|
+
import { Button } from '@<%= projectName %>/ui/button';
|
|
2211
|
+
import { Input } from '@<%= projectName %>/ui/input';
|
|
2212
|
+
import { Checkbox } from '@<%= projectName %>/ui/checkbox';
|
|
2213
|
+
|
|
2214
|
+
interface Todo {
|
|
2215
|
+
id: number;
|
|
2216
|
+
title: string;
|
|
2217
|
+
done: boolean;
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
const api = {
|
|
2221
|
+
getTodos: (): Promise<Todo[]> => fetch('/api/todos').then((r) => r.json()),
|
|
2222
|
+
createTodo: (title: string): Promise<Todo> =>
|
|
2223
|
+
fetch('/api/todos', {
|
|
2224
|
+
method: 'POST',
|
|
2225
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2226
|
+
body: JSON.stringify({ title }),
|
|
2227
|
+
}).then((r) => r.json()),
|
|
2228
|
+
toggleTodo: (id: number): Promise<Todo> =>
|
|
2229
|
+
fetch(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }).then((r) => r.json()),
|
|
2230
|
+
deleteTodo: (id: number): Promise<void> =>
|
|
2231
|
+
fetch(\`/api/todos/\${id}\`, { method: 'DELETE' }).then(() => undefined),
|
|
2232
|
+
};
|
|
2233
|
+
|
|
2234
|
+
export function TodoList({
|
|
2235
|
+
session,
|
|
2236
|
+
}: {
|
|
2237
|
+
session: { user: { email: string } };
|
|
2238
|
+
}) {
|
|
2239
|
+
const queryClient = useQueryClient();
|
|
2240
|
+
|
|
2241
|
+
const { data: todos = [], isPending } = useQuery({
|
|
2242
|
+
queryKey: ['todos'],
|
|
2243
|
+
queryFn: api.getTodos,
|
|
2244
|
+
});
|
|
2245
|
+
|
|
2246
|
+
const createMutation = useMutation({
|
|
2247
|
+
mutationFn: api.createTodo,
|
|
2248
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
2249
|
+
});
|
|
2250
|
+
|
|
2251
|
+
const toggleMutation = useMutation({
|
|
2252
|
+
mutationFn: api.toggleTodo,
|
|
2253
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
2254
|
+
});
|
|
2255
|
+
|
|
2256
|
+
const deleteMutation = useMutation({
|
|
2257
|
+
mutationFn: api.deleteTodo,
|
|
2258
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
2259
|
+
});
|
|
2260
|
+
|
|
2261
|
+
const form = useForm({
|
|
2262
|
+
defaultValues: { title: '' },
|
|
2263
|
+
onSubmit: async ({ value }) => {
|
|
2264
|
+
if (!value.title.trim()) return;
|
|
2265
|
+
createMutation.mutate(value.title.trim());
|
|
2266
|
+
form.reset();
|
|
2267
|
+
},
|
|
2268
|
+
});
|
|
2269
|
+
|
|
2270
|
+
return (
|
|
2271
|
+
<>
|
|
2272
|
+
<div className="flex items-center justify-between mb-6">
|
|
2273
|
+
<h1 className="text-2xl font-semibold">Todos</h1>
|
|
2274
|
+
<div className="flex items-center gap-3 text-sm text-muted-foreground">
|
|
2275
|
+
<span>{session.user.email}</span>
|
|
2276
|
+
<Button
|
|
2277
|
+
variant="ghost"
|
|
2278
|
+
size="sm"
|
|
2279
|
+
onClick={() => authClient.signOut()}
|
|
2280
|
+
>
|
|
2281
|
+
Sign out
|
|
2282
|
+
</Button>
|
|
2283
|
+
</div>
|
|
2284
|
+
</div>
|
|
2285
|
+
|
|
2286
|
+
<form
|
|
2287
|
+
onSubmit={(e) => {
|
|
2288
|
+
e.preventDefault();
|
|
2289
|
+
form.handleSubmit();
|
|
2290
|
+
}}
|
|
2291
|
+
className="flex gap-2 mb-6"
|
|
2292
|
+
>
|
|
2293
|
+
<form.Field name="title">
|
|
2294
|
+
{(field) => (
|
|
2295
|
+
<Input
|
|
2296
|
+
value={field.state.value}
|
|
2297
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
2298
|
+
placeholder="New todo\u2026"
|
|
2299
|
+
className="flex-1"
|
|
2300
|
+
/>
|
|
2301
|
+
)}
|
|
2302
|
+
</form.Field>
|
|
2303
|
+
|
|
2304
|
+
<form.Subscribe selector={(s) => s.isSubmitting}>
|
|
2305
|
+
{(isSubmitting) => (
|
|
2306
|
+
<Button type="submit" disabled={isSubmitting || createMutation.isPending}>
|
|
2307
|
+
Add
|
|
2308
|
+
</Button>
|
|
2309
|
+
)}
|
|
2310
|
+
</form.Subscribe>
|
|
2311
|
+
</form>
|
|
2312
|
+
|
|
2313
|
+
{isPending ? (
|
|
2314
|
+
<p className="text-sm text-muted-foreground">Loading\u2026</p>
|
|
2315
|
+
) : (
|
|
2316
|
+
<ul className="divide-y divide-border">
|
|
2317
|
+
{todos.map((todo) => (
|
|
2318
|
+
<li key={todo.id} className="flex items-center gap-3 py-3">
|
|
2319
|
+
<Checkbox
|
|
2320
|
+
checked={todo.done}
|
|
2321
|
+
onCheckedChange={() => toggleMutation.mutate(todo.id)}
|
|
2322
|
+
/>
|
|
2323
|
+
<span
|
|
2324
|
+
className={\`flex-1 text-sm \${todo.done ? 'line-through text-muted-foreground' : ''}\`}
|
|
2325
|
+
>
|
|
2326
|
+
{todo.title}
|
|
2327
|
+
</span>
|
|
2328
|
+
<Button
|
|
2329
|
+
variant="ghost"
|
|
2330
|
+
size="sm"
|
|
2331
|
+
onClick={() => deleteMutation.mutate(todo.id)}
|
|
2332
|
+
className="text-muted-foreground hover:text-destructive h-7 w-7 p-0"
|
|
2333
|
+
>
|
|
2334
|
+
\xD7
|
|
2335
|
+
</Button>
|
|
2336
|
+
</li>
|
|
2337
|
+
))}
|
|
2338
|
+
</ul>
|
|
2339
|
+
)}
|
|
2340
|
+
|
|
2341
|
+
{!isPending && todos.length === 0 && (
|
|
2342
|
+
<p className="text-sm text-muted-foreground">No todos yet.</p>
|
|
2343
|
+
)}
|
|
2344
|
+
</>
|
|
2345
|
+
);
|
|
2346
|
+
}`
|
|
2347
|
+
};
|
|
2348
|
+
|
|
1912
2349
|
// src/shadcn-ui/templates/accordion.ts
|
|
1913
2350
|
var accordion_default = {
|
|
1914
2351
|
filename: "packages/ui/src/components/accordion.tsx",
|
|
@@ -7435,6 +7872,11 @@ var shadcnUi = {
|
|
|
7435
7872
|
eslint_config_default4,
|
|
7436
7873
|
link_default2,
|
|
7437
7874
|
logo_default2,
|
|
7875
|
+
layout_default2,
|
|
7876
|
+
page_default2,
|
|
7877
|
+
providers_default2,
|
|
7878
|
+
auth_form_default2,
|
|
7879
|
+
todo_list_default2,
|
|
7438
7880
|
accordion_default,
|
|
7439
7881
|
alert_dialog_default,
|
|
7440
7882
|
alert_default,
|
|
@@ -7481,6 +7923,9 @@ var shadcnUi = {
|
|
|
7481
7923
|
toggle_group_default,
|
|
7482
7924
|
toggle_default,
|
|
7483
7925
|
tooltip_default
|
|
7926
|
+
],
|
|
7927
|
+
packages: [
|
|
7928
|
+
{ package: "next-themes", module: "apps/web", version: "^0.4.6" }
|
|
7484
7929
|
]
|
|
7485
7930
|
};
|
|
7486
7931
|
var shadcn_ui_default = shadcnUi;
|