Skip to content

Commit

Permalink
migrate to nextui alert component
Browse files Browse the repository at this point in the history
  • Loading branch information
shawnmclean committed Dec 25, 2024
1 parent f9862e3 commit ca69f83
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 77 deletions.
3 changes: 3 additions & 0 deletions apps/sovoli.com/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ createJiti(fileURLToPath(import.meta.url))("./src/env");

/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
authInterrupts: true,
},
reactStrictMode: true,
images: {
loader: "custom",
Expand Down
37 changes: 37 additions & 0 deletions apps/sovoli.com/src/app/(dashboard)/new/actions/newNoteAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use server";

import { unauthorized } from "next/navigation";
import { withZod } from "@rvf/zod";
import { auth } from "@sovoli/auth";

import { formNewNoteSchema } from "./schemas";

export type State = {
status: "error";
message: string;
errors?: Record<string, string>;
} | null;

const validator = withZod(formNewNoteSchema);

export async function newNoteAction(
_prevState: State,
formData: FormData,
): Promise<State> {
const session = await auth();
if (!session) {
unauthorized();
}

const result = await validator.validate(formData);

if (result.error) {
return {
status: "error",
message: "Failed to create note",
errors: result.error.fieldErrors,
};
}

throw new Error("Not implemented");
}
11 changes: 11 additions & 0 deletions apps/sovoli.com/src/app/(dashboard)/new/actions/schemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { z } from "zod";

/**
* For usage with the form action and form
*/
export const formNewNoteSchema = z.object({
title: z.string(),
description: z.string(),
content: z.string(),
});
export type FormNewNoteSchema = z.infer<typeof formNewNoteSchema>;
43 changes: 42 additions & 1 deletion apps/sovoli.com/src/app/(dashboard)/new/components/NoteForm.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
"use client";

import { useActionState } from "react";
import {
Alert,
AlertDescription,
AlertTitle,
} from "@sovoli/ui/components/alert";
import { Button } from "@sovoli/ui/components/button";
import { Form } from "@sovoli/ui/components/form";
import { Input } from "@sovoli/ui/components/input";

import type { State } from "../actions/newNoteAction";
import { Tiptap } from "~/components/TipTap/Tiptap";
import { newNoteAction } from "../actions/newNoteAction";

export const NoteForm = () => {
const [state, formAction, pending] = useActionState<State, FormData>(
newNoteAction,
null,
);

return (
<Form className="w-full">
<Form className="w-full" action={formAction}>
<Input
placeholder="Title"
name="title"
Expand All @@ -24,6 +40,31 @@ export const NoteForm = () => {
variant="bordered"
/>
<Tiptap />
<div className="flex w-full justify-between gap-2">
<div className="w-full">
{state?.status === "error" && (
<Alert
title={state.message}
color="danger"
description={
<ul>
{Object.entries(state.errors ?? {}).map(([key, value]) => (
<li key={key}>
<strong>{key}</strong>: {value}
</li>
))}
</ul>
}
/>
)}
</div>
<div className="flex gap-2">
<Button isLoading={pending}>Save Draft</Button>
<Button color="primary" type="submit" isLoading={pending}>
Create
</Button>
</div>
</div>
</Form>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@

import type { KnowledgeType } from "@sovoli/db/schema";
import { useActionState, useState } from "react";
import {
Alert,
AlertDescription,
AlertTitle,
} from "@sovoli/ui/components/alert";
import { Alert } from "@sovoli/ui/components/alert";
import { Button } from "@sovoli/ui/components/button";
import {
Card,
Expand Down Expand Up @@ -107,19 +103,19 @@ export const ShelfImportForm = ({ userCollections }: ShelfImportFormProps) => {
</form>
</Card>
{state && (
<Alert variant="danger">
<AlertTitle>{state.status}</AlertTitle>
<AlertDescription>
<p>{state.message}</p>
<Alert
title={state.message}
color="danger"
description={
<ul>
{Object.entries(state.errors ?? {}).map(([key, value]) => (
<li key={key}>
<strong>{key}</strong>: {value}
</li>
))}
</ul>
</AlertDescription>
</Alert>
}
/>
)}
</section>
);
Expand Down Expand Up @@ -167,12 +163,7 @@ const SelectFileStep = ({ onValidFileSelected }: SelectFileStepProps) => {
) : (
<>
<CSVFileInput name="csvFile" onFileDropped={handleFileDropped} />{" "}
{error && (
<Alert variant="danger">
<AlertTitle>Error</AlertTitle>
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{error && <Alert color="danger" title="Error" description={error} />}
</>
)}
</section>
Expand Down
1 change: 1 addition & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
},
"dependencies": {
"@nextui-org/avatar": "2.2.4",
"@nextui-org/alert": "2.2.8",
"@nextui-org/badge": "2.2.3",
"@nextui-org/breadcrumbs": "2.2.4",
"@nextui-org/button": "2.2.7",
Expand Down
60 changes: 1 addition & 59 deletions packages/ui/src/components/alert/index.tsx
Original file line number Diff line number Diff line change
@@ -1,59 +1 @@
import * as React from "react";
import { tv } from "tailwind-variants";

// Tailwind Variants for Alert Container
const alertContainer = tv({
base: "relative w-full rounded-lg border p-4 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
variants: {
variant: {
default: "bg-background text-foreground",
danger:
"border-danger/50 text-danger dark:border-danger [&>svg]:text-danger",
},
},
defaultVariants: {
variant: "default",
},
});

const alertTitleStyles = tv({
base: "mb-1 font-medium leading-none tracking-tight",
});

const alertDescriptionStyles = tv({
base: "text-sm [&_p]:leading-relaxed",
});

// Alert Component
const Alert = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & { variant?: "default" | "danger" }
>(({ className, variant = "default", ...props }, ref) => (
<div
ref={ref}
role="alert"
className={alertContainer({ variant, className })}
{...props}
/>
));
Alert.displayName = "Alert";

// AlertTitle Component
const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5 ref={ref} className={alertTitleStyles({ className })} {...props} />
));
AlertTitle.displayName = "AlertTitle";

// AlertDescription Component
const AlertDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={alertDescriptionStyles({ className })} {...props} />
));
AlertDescription.displayName = "AlertDescription";

export { Alert, AlertTitle, AlertDescription };
export * from "@nextui-org/alert";
Loading

0 comments on commit ca69f83

Please sign in to comment.