mirror of
https://gitlab.com/MisterBiggs/hello-remix.git
synced 2025-08-03 20:21:33 +00:00
I got bored and just started reading but fun tut
This commit is contained in:
102
app/routes/contacts.$contactId.tsx
Normal file
102
app/routes/contacts.$contactId.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import { Form, useLoaderData } from "@remix-run/react";
|
||||
import type { FunctionComponent } from "react";
|
||||
import type { LoaderFunctionArgs } from "@remix-run/node";
|
||||
|
||||
import type { ContactRecord } from "../data";
|
||||
import { getContact } from "../data";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export const loader = async ({
|
||||
params,
|
||||
}: LoaderFunctionArgs) => {
|
||||
invariant(params.contactId, "Missing contactId param");
|
||||
const contact = await getContact(params.contactId);
|
||||
if (!contact) {
|
||||
throw new Response("Not Found", {status: 404});
|
||||
}
|
||||
return { contact };
|
||||
};
|
||||
|
||||
export default function Contact() {
|
||||
const { contact } = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<div id="contact">
|
||||
<div>
|
||||
<img
|
||||
alt={`${contact.first} ${contact.last} avatar`}
|
||||
key={contact.avatar}
|
||||
src={contact.avatar}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h1>
|
||||
{contact.first || contact.last ? (
|
||||
<>
|
||||
{contact.first} {contact.last}
|
||||
</>
|
||||
) : (
|
||||
<i>No Name</i>
|
||||
)}{" "}
|
||||
<Favorite contact={contact} />
|
||||
</h1>
|
||||
|
||||
{contact.twitter ? (
|
||||
<p>
|
||||
<a
|
||||
href={`https://twitter.com/${contact.twitter}`}
|
||||
>
|
||||
{contact.twitter}
|
||||
</a>
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
{contact.notes ? <p>{contact.notes}</p> : null}
|
||||
|
||||
<div>
|
||||
<Form action="edit">
|
||||
<button type="submit">Edit</button>
|
||||
</Form>
|
||||
|
||||
<Form
|
||||
action="destroy"
|
||||
method="post"
|
||||
onSubmit={(event) => {
|
||||
const response = confirm(
|
||||
"Please confirm you want to delete this record."
|
||||
);
|
||||
if (!response) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<button type="submit">Delete</button>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const Favorite: FunctionComponent<{
|
||||
contact: Pick<ContactRecord, "favorite">;
|
||||
}> = ({ contact }) => {
|
||||
const favorite = contact.favorite;
|
||||
|
||||
return (
|
||||
<Form method="post">
|
||||
<button
|
||||
aria-label={
|
||||
favorite
|
||||
? "Remove from favorites"
|
||||
: "Add to favorites"
|
||||
}
|
||||
name="favorite"
|
||||
value={favorite ? "false" : "true"}
|
||||
>
|
||||
{favorite ? "★" : "☆"}
|
||||
</button>
|
||||
</Form>
|
||||
);
|
||||
};
|
13
app/routes/contacts.$contactId_.destroy.tsx
Normal file
13
app/routes/contacts.$contactId_.destroy.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { redirect } from "@remix-run/node";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
import { deleteContact } from "../data";
|
||||
|
||||
export const action = async ({
|
||||
params,
|
||||
}: ActionFunctionArgs) => {
|
||||
invariant(params.contactId, "Missing contactId param");
|
||||
await deleteContact(params.contactId);
|
||||
return redirect("/");
|
||||
};
|
85
app/routes/contacts.$contactId_.edit.tsx
Normal file
85
app/routes/contacts.$contactId_.edit.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import type { LoaderFunctionArgs, ActionFunctionArgs } from "@remix-run/node";
|
||||
import { Form, useLoaderData } from "@remix-run/react";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
import { getContact, updateContact } from "../data";
|
||||
import { redirect } from "@remix-run/node";
|
||||
|
||||
export const loader = async ({
|
||||
params,
|
||||
}: LoaderFunctionArgs) => {
|
||||
invariant(params.contactId, "Missing contactId param");
|
||||
const contact = await getContact(params.contactId);
|
||||
if (!contact) {
|
||||
throw new Response("Not Found", { status: 404 });
|
||||
}
|
||||
return { contact };
|
||||
};
|
||||
|
||||
export const action = async ({
|
||||
params,
|
||||
request,
|
||||
}: ActionFunctionArgs) => {
|
||||
invariant(params.contactId, "Missing contactId param");
|
||||
const formData = await request.formData();
|
||||
const updates = Object.fromEntries(formData);
|
||||
await updateContact(params.contactId, updates);
|
||||
return redirect(`/contacts/${params.contactId}`);
|
||||
};
|
||||
|
||||
export default function EditContact() {
|
||||
const { contact } = useLoaderData<typeof loader>();
|
||||
|
||||
return (
|
||||
<Form key={contact.id} id="contact-form" method="post">
|
||||
<p>
|
||||
<span>Name</span>
|
||||
<input
|
||||
aria-label="First name"
|
||||
defaultValue={contact.first}
|
||||
name="first"
|
||||
placeholder="First"
|
||||
type="text"
|
||||
/>
|
||||
<input
|
||||
aria-label="Last name"
|
||||
defaultValue={contact.last}
|
||||
name="last"
|
||||
placeholder="Last"
|
||||
type="text"
|
||||
/>
|
||||
</p>
|
||||
<label>
|
||||
<span>Twitter</span>
|
||||
<input
|
||||
defaultValue={contact.twitter}
|
||||
name="twitter"
|
||||
placeholder="@jack"
|
||||
type="text"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>Avatar URL</span>
|
||||
<input
|
||||
aria-label="Avatar URL"
|
||||
defaultValue={contact.avatar}
|
||||
name="avatar"
|
||||
placeholder="https://example.com/avatar.jpg"
|
||||
type="text"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>Notes</span>
|
||||
<textarea
|
||||
defaultValue={contact.notes}
|
||||
name="notes"
|
||||
rows={6}
|
||||
/>
|
||||
</label>
|
||||
<p>
|
||||
<button type="submit">Save</button>
|
||||
<button type="button">Cancel</button>
|
||||
</p>
|
||||
</Form>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user