@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.
Files changed (2) hide show
  1. package/dist/index.js +445 -0
  2. 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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newt-app/templates",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "private": false,
5
5
  "description": "Templates for newt-app",
6
6
  "type": "module",