Skip to content

Commit

Permalink
Merge pull request #53 from emiliosheinz/feat/enable-create-shelters
Browse files Browse the repository at this point in the history
feat: enable create shelters
  • Loading branch information
emiliosheinz authored Jun 4, 2024
2 parents 60d9604 + 2878cad commit 30cf29c
Show file tree
Hide file tree
Showing 25 changed files with 905 additions and 200 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@radix-ui/react-slot": "^1.0.2",
"@t3-oss/env-nextjs": "^0.10.1",
"@tanstack/react-query": "^5.25.0",
"@tanstack/react-table": "^8.17.3",
"@trpc/client": "next",
"@trpc/react-query": "next",
"@trpc/server": "next",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
Warnings:
- A unique constraint covering the columns `[uuid]` on the table `Shelter` will be added. If there are existing duplicate values, this will fail.
- Made the column `uuid` on table `Shelter` required. This step will fail if there are existing NULL values in that column.
*/
-- AlterTable
ALTER TABLE "Shelter" ALTER COLUMN "uuid" SET NOT NULL;

-- CreateIndex
CREATE UNIQUE INDEX "Shelter_uuid_key" ON "Shelter"("uuid");
2 changes: 1 addition & 1 deletion prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ datasource db {

model Shelter {
id Int @id @default(autoincrement())
uuid String? @default(cuid())
uuid String @unique @default(cuid())
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Expand Down
5 changes: 5 additions & 0 deletions scripts/populateUUIDs.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// @ts-nocheck

// This script will populate the `uuid` field for all shelters that do not have one.
// This script should be run once to populate the `uuid` field for all shelters.

import { PrismaClient } from "@prisma/client";
import cuid from "cuid";

Expand Down
56 changes: 56 additions & 0 deletions src/app/user/shelters/[uuid]/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"use client";

import { api } from "~/trpc/react";
import { FormEditRegister } from "~/app/user/shelters/_components";
import { Skeleton } from "~/components/ui/skeleton";
import { notFound } from "next/navigation";
import { FiAlertTriangle } from "react-icons/fi";

import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert";

export default function EditShelterPage({
params,
}: {
params: { uuid: string };
}) {
const { data, isLoading, error } =
api.userShelters.findUserShelterByUuid.useQuery({
uuid: params.uuid,
});

if (error) {
if (error?.data?.code === "NOT_FOUND") {
return notFound();
}

return (
<main className="mx-auto max-w-7xl bg-white px-4">
<div className="m-auto flex w-full max-w-2xl flex-col flex-wrap gap-3 pt-6">
<Alert variant="destructive">
<FiAlertTriangle />
<AlertTitle>Erro</AlertTitle>
<AlertDescription>
Ocorreu um erro ao carregar os dados do abrigo. Por favor, tente
novamente mais tarde.
</AlertDescription>
</Alert>
</div>
</main>
);
}

if (isLoading) {
return (
<main className="mx-auto max-w-7xl bg-white px-4">
<div className="m-auto flex w-full max-w-2xl flex-col flex-wrap gap-3 pt-6">
<Skeleton className="h-[33px] w-[180px] rounded-xl" />
<Skeleton className="h-[300px] w-full rounded-xl" />
<Skeleton className="h-[300px] w-full rounded-xl" />
<Skeleton className="h-[300px] w-full rounded-xl" />
</div>
</main>
);
}

return <FormEditRegister shelter={data} />;
}
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"use client";

import { Button } from "~/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "~/components/ui/dialog";

import { toast } from "sonner";

import { FiTrash } from "react-icons/fi";
import { useState } from "react";
import { api } from "~/trpc/react";
import { useRouter } from "next/navigation";

export function DialogDelete({ shelterUuid }: { shelterUuid: string }) {
const router = useRouter();
const [open, setOpen] = useState(false);

const deleteShelter = api.userShelters.delete.useMutation({
onSuccess: () => {
toast.success("Abrigo excluído com sucesso!");
router.replace("/user/shelters");
setOpen(false);
},
onError: (error) => {
setOpen(false);
toast.error("Ops! Houve um erro ao excluir o abrigo.");
console.error(error);
},
});

return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button type="button" variant="destructive">
<FiTrash className="mr-2" />
Excluir
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Atenção</DialogTitle>
<DialogDescription>
Você tem certeza que deseja excluir este abrigo? Esta ação não pode
ser desfeita.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button
type="button"
variant="secondary"
onClick={() => setOpen(false)}
>
Cancelar
</Button>
<Button
type="button"
onClick={() => {
deleteShelter.mutate({ uuid: shelterUuid });
}}
>
Excluir
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"use client";

import { useState } from "react";
import { useForm } from "react-hook-form";
import { Loader2 } from "lucide-react";
import { toast } from "sonner";
Expand All @@ -18,22 +17,24 @@ import {
FormDescription,
} from "~/components/ui/form";
import { cepMask, phoneMask, socialMediaMask } from "~/lib/masks";
import { shelterSchema } from "~/schemas/shelter";
import { shelterSchema, type apiShelterSchema } from "~/schemas/shelter";
import { api } from "~/trpc/react";
import { useRouter } from "next/navigation";
import { TagInput } from "~/components/tag-input";
import { defaultValues } from "./constants";
import {
ShelterContextProvider,
useShelterContext,
} from "~/contexts/ShelterContext";
import { Card as CardBase, CardContent } from "~/components/ui/card";
import { DialogDelete } from "./dialog-delete";
import { googleMaps } from "~/lib/google-maps";
import { useState } from "react";

function Shelter() {
interface FormEditRegisterProps {
shelter?: z.infer<typeof apiShelterSchema> | null;
}

export function FormEditRegister({ shelter }: FormEditRegisterProps = {}) {
const [isGettingCoordinates, setIsGettingCoordinates] =
useState<boolean>(false);
const { shelter } = useShelterContext();

const form = useForm<z.infer<typeof shelterSchema>>({
resolver: zodResolver(shelterSchema),
defaultValues: {
Expand All @@ -42,31 +43,28 @@ function Shelter() {
},
});
const router = useRouter();
const createShelter = api.shelter.create.useMutation({
const createShelter = api.userShelters.create.useMutation({
onSuccess: () => {
router.replace("/");
router.replace("/user/shelters");
toast.success("Abrigo criado com sucesso!");
},
onError: (error) => {
toast.error("Ops! Houve um erro ao criar o abrigo.");
console.error(error);
},
});
const updateCurrentUserShelter =
api.shelter.updateCurrentUserShelter.useMutation({
onSuccess: () => {
toast.success("Abrigo atualizado com sucesso!");
window.scrollTo(0, 0);
},
onError: (error) => {
toast.error("Ops! Houve um erro ao atualizar o abrigo.");
console.error(error);
},
});
const updateShelter = api.userShelters.update.useMutation({
onSuccess: () => {
toast.success("Abrigo atualizado com sucesso!");
window.scrollTo(0, 0);
},
onError: (error) => {
toast.error("Ops! Houve um erro ao atualizar o abrigo.");
console.error(error);
},
});
const isLoading =
createShelter.isPending ||
updateCurrentUserShelter.isPending ||
isGettingCoordinates;
createShelter.isPending || updateShelter.isPending || isGettingCoordinates;
const isEditing = !!shelter;
const hasModifiedInputs = Object.keys(form.formState.dirtyFields).length > 0;

Expand All @@ -81,7 +79,7 @@ function Shelter() {
state,
});

const shelter = {
const formShelter = {
...values,
address: {
...values.address,
Expand All @@ -90,8 +88,17 @@ function Shelter() {
},
};

const mutation = isEditing ? updateCurrentUserShelter : createShelter;
mutation.mutate(shelter);
if (isEditing) {
updateShelter.mutate({
...formShelter,
id: shelter.id,
uuid: shelter.uuid,
});

return;
}

createShelter.mutate(formShelter);
} catch (error) {
toast.error(
"Ops! Houve um erro ao buscar as coordenadas do endereço e o abrigo não foi criado. Tente novamente!.",
Expand Down Expand Up @@ -428,34 +435,19 @@ function Shelter() {
</CardContent>
</CardBase>

<Button
type="submit"
className="w-full"
disabled={isLoading || (isEditing && !hasModifiedInputs)}
>
{isLoading ? <Loader2 className="animate-spin" /> : "Salvar"}
</Button>
<div className="flex gap-5">
<Button
type="submit"
className="w-full flex-1"
disabled={isLoading || (isEditing && !hasModifiedInputs)}
>
{isLoading ? <Loader2 className="animate-spin" /> : "Salvar"}
</Button>
{isEditing && <DialogDelete shelterUuid={shelter.uuid} />}
</div>
</form>
</Form>
</div>
</main>
);
}

export default function ShelterPage() {
const { data, isLoading } = api.shelter.findCurrentUserShelter.useQuery();

if (isLoading) {
return (
<div className="flex w-full justify-center pt-28">
<Loader2 className="size-8 animate-spin" />
</div>
);
}

return (
<ShelterContextProvider shelter={data ?? null}>
<Shelter />
</ShelterContextProvider>
);
}
2 changes: 2 additions & 0 deletions src/app/user/shelters/_components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./shelters-list-table";
export * from "./form-edit-register";
Loading

0 comments on commit 30cf29c

Please sign in to comment.