Skip to content

Commit f17e807

Browse files
committed
feat: Adding solid-ui, sentry and form
1 parent 13f8ef1 commit f17e807

File tree

20 files changed

+478
-5
lines changed

20 files changed

+478
-5
lines changed

src/create-app.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,14 @@ export async function createApp(options: Required<Options>) {
273273
await mkdir(resolve(targetDir, 'src/components'), { recursive: true })
274274
}
275275

276+
// Check for a .cursorrules file
277+
if (existsSync(resolve(templateDirBase, '.cursorrules'))) {
278+
await copyFile(
279+
resolve(templateDirBase, '.cursorrules'),
280+
resolve(targetDir, '.cursorrules'),
281+
)
282+
}
283+
276284
// Copy in Vite and Tailwind config and CSS
277285
if (!options.tailwind) {
278286
await copyFiles(templateDirBase, ['./src/App.css'])
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { createFileRoute } from '@tanstack/solid-router'
2+
3+
import { createForm } from '@tanstack/solid-form'
4+
import type { AnyFieldApi } from '@tanstack/solid-form'
5+
6+
interface FieldInfoProps {
7+
field: AnyFieldApi
8+
}
9+
10+
export const Route = createFileRoute('/demo/form')({
11+
component: FormExample,
12+
})
13+
14+
function FieldInfo(props: FieldInfoProps) {
15+
return (
16+
<>
17+
{props.field.state.meta.isTouched &&
18+
props.field.state.meta.errors.length ? (
19+
<em>{props.field.state.meta.errors.join(',')}</em>
20+
) : null}
21+
{props.field.state.meta.isValidating ? 'Validating...' : null}
22+
</>
23+
)
24+
}
25+
26+
function FormExample() {
27+
const form = createForm(() => ({
28+
defaultValues: {
29+
firstName: '',
30+
lastName: '',
31+
},
32+
onSubmit: async ({ value }) => {
33+
// Do something with form data
34+
console.log(value)
35+
},
36+
}))
37+
38+
return (
39+
<div class="max-w-md mx-auto mt-10 p-6 bg-white rounded-lg shadow-md">
40+
<h1 class="text-2xl font-bold mb-8 text-gray-800 leading-tight">
41+
Simple Form Example
42+
</h1>
43+
<form
44+
class="space-y-6"
45+
onSubmit={(e) => {
46+
e.preventDefault()
47+
e.stopPropagation()
48+
form.handleSubmit()
49+
}}
50+
>
51+
<div class="space-y-1">
52+
<form.Field
53+
name="firstName"
54+
validators={{
55+
onChange: ({ value }) =>
56+
!value
57+
? 'A first name is required'
58+
: value.length < 3
59+
? 'First name must be at least 3 characters'
60+
: undefined,
61+
onChangeAsyncDebounceMs: 500,
62+
onChangeAsync: async ({ value }) => {
63+
await new Promise((resolve) => setTimeout(resolve, 1000))
64+
return (
65+
value.includes('error') && 'No "error" allowed in first name'
66+
)
67+
},
68+
}}
69+
children={(field) => {
70+
return (
71+
<>
72+
<label
73+
for={field().name}
74+
class="block text-sm font-medium text-gray-700 mb-1 leading-tight"
75+
>
76+
First Name:
77+
</label>
78+
<input
79+
id={field().name}
80+
name={field().name}
81+
value={field().state.value}
82+
onBlur={field().handleBlur}
83+
onInput={(e) => field().handleChange(e.target.value)}
84+
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm leading-relaxed p-2"
85+
/>
86+
<FieldInfo field={field()} />
87+
</>
88+
)
89+
}}
90+
/>
91+
</div>
92+
<div class="space-y-1">
93+
<form.Field
94+
name="lastName"
95+
children={(field) => (
96+
<>
97+
<label
98+
for={field().name}
99+
class="block text-sm font-medium text-gray-700 mb-1 leading-tight"
100+
>
101+
Last Name:
102+
</label>
103+
<input
104+
id={field().name}
105+
name={field().name}
106+
value={field().state.value}
107+
onBlur={field().handleBlur}
108+
onInput={(e) => field().handleChange(e.target.value)}
109+
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm leading-relaxed p-2"
110+
/>
111+
<FieldInfo field={field()} />
112+
</>
113+
)}
114+
/>
115+
</div>
116+
<form.Subscribe
117+
selector={(state) => ({
118+
canSubmit: state.canSubmit,
119+
isSubmitting: state.isSubmitting,
120+
})}
121+
children={(state) => {
122+
return (
123+
<button
124+
type="submit"
125+
disabled={!state().canSubmit}
126+
class="w-full flex justify-center py-2.5 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed leading-tight"
127+
>
128+
{state().isSubmitting ? '...' : 'Submit'}
129+
</button>
130+
)
131+
}}
132+
/>
133+
</form>
134+
</div>
135+
)
136+
}

templates/solid/add-on/form/info.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "Form",
3+
"description": "TansStack Form",
4+
"phase": "add-on",
5+
"link": "https://tanstack.com/form/latest",
6+
"routes": [
7+
{
8+
"url": "/demo/form",
9+
"name": "Form"
10+
}
11+
]
12+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"dependencies": {
3+
"@tanstack/solid-form": "^0.43.2"
4+
}
5+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
We use Sentry for watching for errors in our deployed application, as well as for instrumentation of our application.
2+
3+
## Error collection
4+
5+
Error collection is automatic and configured in `src/router.tsx`.
6+
7+
## Instrumentation
8+
9+
We want our server functions intstrumented. So if you see a function name like `createServerFn`, you can instrument it with Sentry. You'll need to import `Sentry`:
10+
11+
```tsx
12+
import * as Sentry from '@sentry/browser'
13+
```
14+
15+
And then wrap the implementation of the server function with `Sentry.startSpan`, liks so:
16+
17+
```tsx
18+
Sentry.startSpan({ name: 'Requesting all the pokemon' }, async () => {
19+
// Some lengthy operation here
20+
await fetch('https://api.pokemon.com/data/')
21+
})
22+
```
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Your Sentry DSN (from your Sentry account)
2+
VITE_SENTRY_DSN=
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { createFileRoute } from '@tanstack/solid-router'
2+
3+
export const Route = createFileRoute('/demo/sentry/bad-event-handler')({
4+
component: RouteComponent,
5+
})
6+
7+
function RouteComponent() {
8+
return (
9+
<div className="p-4">
10+
<button
11+
type="button"
12+
onClick={() => {
13+
throw new Error('Sentry Frontend Error')
14+
}}
15+
>
16+
Throw error
17+
</button>
18+
</div>
19+
)
20+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "Sentry",
3+
"phase": "setup",
4+
"description": "Add Sentry for error monitoring and crash reporting (requires Start).",
5+
"link": "https://sentry.com/",
6+
"routes": [
7+
{
8+
"url": "/demo/sentry/bad-event-handler",
9+
"name": "Sentry"
10+
}
11+
]
12+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"dependencies": {
3+
"@sentry/solid": "^9.1.0"
4+
}
5+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
## Solid-UI
2+
3+
This installation of Solid-UI follows the manual instructions but was modified to work with Tailwind V4.
4+
5+
To install the components, run the following command (this install button):
6+
7+
```bash
8+
npx solidui-cli@latest add button
9+
```

0 commit comments

Comments
 (0)