Skip to content

Commit dac32ba

Browse files
committed
Update Nuxt, some other dependencies, plus improvements to codebase
- Update to Nuxt 4.1 - Update other dependencies to latest versions (aside from Drizzle) - Update code to include more real-world examples, such as shared validation and server routes
1 parent f3ff529 commit dac32ba

File tree

22 files changed

+816
-919
lines changed

22 files changed

+816
-919
lines changed

.npmrc

Lines changed: 0 additions & 1 deletion
This file was deleted.

README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
1-
# Angry Nuxt Template
1+
# 😡 Angry Nuxt Template
22

3-
This is a template for a Nuxt 4\* project.
3+
Easiest way to start and deploy a Nuxt 4 project on Cloudflare, via Nuxt Hub.
44

5-
> \* Nuxt 4 has not been released yet, but we already have feature flags and
6-
> directory structure that are compatible with it.
5+
[![Deploy to NuxtHub](https://hub.nuxt.com/button.svg)](https://hub.nuxt.com/new?template=raggesilver/angry-nuxt)
76

87
## What is included?
98

10-
- [x] Nuxt 4 compatible
119
- [x] Nuxt SEO
1210
- [x] [UnoCSS](https://unocss.dev/) — Tailwind CSS compatible, but faster and more feature-rich
1311
- [x] [Shadcn Vue](https://shadcn-vue.com) — Pre-configured Shadcn components
1412
- Includes Zod and VeeValidate for fully typed forms
1513
- [x] [Drizzle ORM](https://orm.drizzle.team) with Cloudflare D1
1614
- Automatic migrations in development ✅
1715
- [x] Prettier + ESLint
16+
- [x] Nuxt Hub — Easy Cloudflare deployments
1817

1918
## Setup
2019

2120
```bash
22-
npx nuxi@latest init --template raggesilver/angry-nuxt-template
21+
bunx nuxt@latest init --template raggesilver/angry-nuxt
2322
```

app/app.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
<script setup lang="ts">
2+
import { Toaster } from "~/components/ui/sonner";
3+
4+
import "vue-sonner/style.css";
5+
</script>
6+
17
<template>
28
<NuxtLoadingIndicator />
39
<NuxtRouteAnnouncer />
410
<NuxtLayout>
511
<NuxtPage />
12+
<Toaster rich-colors />
613
</NuxtLayout>
714
</template>

app/assets/css/main.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
:root {
2-
--primary: 60% 100% 50%;
1+
* {
2+
@apply scroll-smooth;
33
}
44

55
body {

app/components/sign-up-form.vue

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<script setup lang="ts">
2+
import { signUpSchema } from "#shared/validation/sign-up";
3+
import { toTypedSchema } from "@vee-validate/zod";
4+
import { LoaderIcon } from "lucide-vue-next";
5+
import { useForm } from "vee-validate";
6+
import { toast } from "vue-sonner";
7+
import { isFetchError } from "~/lib/is-ofetch-error";
8+
9+
const validationSchema = toTypedSchema(signUpSchema);
10+
11+
const form = useForm({
12+
validationSchema,
13+
initialValues: {
14+
name: "",
15+
tos: false,
16+
},
17+
});
18+
19+
const isSubmitting = computed(() => form.isSubmitting.value);
20+
21+
const onSubmit = form.handleSubmit(async (values, { resetForm }) => {
22+
try {
23+
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate network delay
24+
const response = await $fetch("/sign-up", { method: "POST", body: values });
25+
toast.success(response.message);
26+
resetForm();
27+
} catch (error) {
28+
if (isFetchError(error)) {
29+
toast.error(error.statusMessage ?? error.message);
30+
} else {
31+
toast.error("An unexpected error occurred.");
32+
console.error(error);
33+
}
34+
}
35+
});
36+
</script>
37+
38+
<template>
39+
<form @submit="onSubmit" @reset="form.handleReset">
40+
<div>
41+
<p class="text-xl font-bold">Sign Up</p>
42+
<p class="text-sm text-muted-foreground">
43+
Create an account to get started.
44+
</p>
45+
</div>
46+
<FormField v-slot="{ componentField }" name="name">
47+
<FormItem>
48+
<FormLabel>Name</FormLabel>
49+
<FormControl>
50+
<Input type="text" v-bind="componentField" placeholder="Jane Doe" />
51+
</FormControl>
52+
<FormMessage />
53+
</FormItem>
54+
</FormField>
55+
<FormField v-slot="{ value, handleChange }" type="checkbox" name="tos">
56+
<FormItem>
57+
<div class="space-y-4 leading-none">
58+
<div class="flex items-center space-x-2">
59+
<FormControl>
60+
<Checkbox
61+
:model-value="value"
62+
@update:model-value="handleChange"
63+
/>
64+
</FormControl>
65+
<FormLabel
66+
class="text-sm font-normal leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
67+
>I agree to the terms of service.</FormLabel
68+
>
69+
</div>
70+
<FormMessage />
71+
</div>
72+
</FormItem>
73+
</FormField>
74+
<div class="grid lt-sm:grid-cols-1 sm:grid-cols-2 gap-2">
75+
<Button type="reset" variant="secondary" :disabled="isSubmitting"
76+
>Reset</Button
77+
>
78+
<Button
79+
type="submit"
80+
class="flex items-center gap-2"
81+
:disabled="isSubmitting"
82+
>
83+
<span>Submit</span>
84+
<LoaderIcon v-if="isSubmitting" class="animate-spin w-[1em]" />
85+
</Button>
86+
</div>
87+
88+
<span
89+
aria-hidden="true"
90+
class="absolute w-3 h-3 rounded-full bg-primary/30 -top-1.5 -right-1.5 animate-ping"
91+
></span>
92+
93+
<span
94+
aria-hidden="true"
95+
class="absolute w-2 h-2 rounded-full bg-primary -top-1 -right-1"
96+
></span>
97+
</form>
98+
</template>

app/components/ui/button/Button.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<script setup lang="ts">
22
import { cn } from "@/lib/utils";
3-
import { Primitive, type PrimitiveProps } from "reka-ui";
3+
import type { PrimitiveProps } from "reka-ui";
4+
import { Primitive } from "reka-ui";
45
import type { HTMLAttributes } from "vue";
5-
import { type ButtonVariants, buttonVariants } from ".";
6+
import type { ButtonVariants } from ".";
7+
import { buttonVariants } from ".";
68
79
interface Props extends PrimitiveProps {
810
variant?: ButtonVariants["variant"];

app/components/ui/button/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { cva, type VariantProps } from "class-variance-authority";
1+
import type { VariantProps } from "class-variance-authority";
2+
import { cva } from "class-variance-authority";
23

34
export { default as Button } from "./Button.vue";
45

app/components/ui/form/FormItem.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<script lang="ts" setup>
22
import { cn } from "@/lib/utils";
33
import { useId } from "reka-ui";
4-
import { type HTMLAttributes, provide } from "vue";
4+
import type { HTMLAttributes } from "vue";
5+
import { provide } from "vue";
56
import { FORM_ITEM_INJECTION_KEY } from "./injectionKeys";
67
78
const props = defineProps<{

app/components/ui/form/FormLabel.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const { error, formItemId } = useFormField();
1414
<Label
1515
data-slot="form-label"
1616
:data-error="!!error"
17-
:class="cn('data-[error=true]:text-destructive-foreground', props.class)"
17+
:class="cn('data-[error=true]:text-destructive', props.class)"
1818
:for="formItemId"
1919
>
2020
<slot />

app/components/ui/form/FormMessage.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<script lang="ts" setup>
22
import { cn } from "@/lib/utils";
33
import { ErrorMessage } from "vee-validate";
4-
import { type HTMLAttributes, toValue } from "vue";
4+
import type { HTMLAttributes } from "vue";
5+
import { toValue } from "vue";
56
import { useFormField } from "./useFormField";
67
78
const props = defineProps<{
@@ -17,6 +18,6 @@ const { name, formMessageId } = useFormField();
1718
data-slot="form-message"
1819
as="p"
1920
:name="toValue(name)"
20-
:class="cn('text-destructive-foreground text-sm', props.class)"
21+
:class="cn('text-destructive text-sm', props.class)"
2122
/>
2223
</template>

0 commit comments

Comments
 (0)