diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..368191c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY package*.json . + +RUN npm install + +COPY . . + +EXPOSE 3000 + +CMD npm run dev \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index 4678774..3fe749b 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,4 +1,13 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + images: { + remotePatterns: [ + { + protocol: 'http', + hostname: 'localhost', + }, + ], + } +}; export default nextConfig; diff --git a/src/actions/admin.ts b/src/actions/admin.ts index ead869f..f2b9c97 100644 --- a/src/actions/admin.ts +++ b/src/actions/admin.ts @@ -3,22 +3,28 @@ import { REVIEWS_API } from "@/lib/constants"; export const login = async (email: string, password: string) => { - const response = await fetch(`${REVIEWS_API}/admin-users/logIn`, { - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ thisUser: email, pass: password }), - method: "POST", - }); - - const data = await response.json(); + try { + const response = await fetch(`${REVIEWS_API}/admin-users/logIn`, { + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ thisUser: email, pass: password }), + method: "POST", + }); - if (data.statusCode === 401) { + const data = await response.json(); + + if (data.statusCode === 401) { + return { + error: "Acceso no autorizado", + }; + } return { - error: "Acceso no autorizado", + token: data.access_token, }; + } catch (error) { + return { + error: "Error al intentar iniciar sesión, intente más tarde", + } } - return { - token: data.access_token, - }; }; diff --git a/src/actions/reviews.ts b/src/actions/reviews.ts index f338c84..018a5a7 100644 --- a/src/actions/reviews.ts +++ b/src/actions/reviews.ts @@ -10,42 +10,41 @@ export const getReviews = async (): Promise => { try { const response = await fetch(`${REVIEWS_API}/reviews`); const { data } = await response.json(); - return data; + return data ?? []; } catch (_) { return []; } }; -export const createReview = async ( - review: z.infer, - image: File, - token: string -) => { - const newReview = { - review: review.review, - rating: String(review.rating), - user: review.user, - date: new Date(), - }; - - const response = await fetch(`${REVIEWS_API}/reviews`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - body: JSON.stringify(newReview), - }); - if (!response.ok) { +export const createReview = async (formData: FormData, token: string) => { + try { + const response = await fetch(`${REVIEWS_API}/reviews`, { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + }, + body: formData, + }); + const data = await response.json(); + if (!response.ok && response.status == 401) { + return { + error: "Su sesión ha expirado, ingrese nuevamente", + }; + } + if (!response.ok) { + return { + error: data.message, + }; + } + revalidatePath("atc24$rw/admin"); + revalidatePath("/"); return { - error: "Su sesión ha expirado, ingrese nuevamente", + success: "Se creó una nueva reseña", + }; + } catch (error) { + return { + error: "Error al crear la reseña", }; } - - revalidatePath("atc24$rw/admin"); - revalidatePath("/"); - return { - success: "Se creó una nueva reseña", - }; }; export const deleteReview = async (id: string, token: string) => { const response = await fetch(`${REVIEWS_API}/reviews/${id}`, { @@ -54,31 +53,42 @@ export const deleteReview = async (id: string, token: string) => { Authorization: `Bearer ${token}`, }, }); - if (!response.ok) { + const data = await response.json(); + if (!response.ok && response.status == 401) { return { error: "Su sesión ha expirado, ingrese nuevamente", }; } + if (!response.ok) { + return { + error: data.message, + }; + } revalidatePath("atc24$rw/admin"); revalidatePath("/"); return { success: "Se eliminó la reseña", }; }; -export const updateReview = async (updatedReview: Review, token: string) => { +export const updateReview = async (formData: FormData, token: string) => { const response = await fetch(`${REVIEWS_API}/reviews`, { method: "PATCH", headers: { Authorization: `Bearer ${token}`, - "Content-Type": "application/json", }, - body: JSON.stringify(updatedReview), + body: formData, }); - if (!response.ok) { + const data = await response.json(); + if (!response.ok && response.status == 401) { return { error: "Su sesión ha expirado, ingrese nuevamente", }; } + if (!response.ok) { + return { + error: data.message, + }; + } revalidatePath("atc24$rw/admin"); revalidatePath("/"); diff --git a/src/app/(admin)/atc24$rw/admin/components/DeleteAlert.tsx b/src/app/(admin)/atc24$rw/admin/components/DeleteAlert.tsx index ef5ad88..69734d2 100644 --- a/src/app/(admin)/atc24$rw/admin/components/DeleteAlert.tsx +++ b/src/app/(admin)/atc24$rw/admin/components/DeleteAlert.tsx @@ -17,7 +17,7 @@ interface DeleteAlertProps { export const DeleteAlert = ({ children, onDelete }: DeleteAlertProps) => { return ( - {children} + {children} @@ -26,7 +26,7 @@ export const DeleteAlert = ({ children, onDelete }: DeleteAlertProps) => { Cancelar - Confirmar + Confirmar diff --git a/src/app/(admin)/atc24$rw/admin/components/ReviewForm.tsx b/src/app/(admin)/atc24$rw/admin/components/ReviewForm.tsx index dee6432..70cf3fd 100644 --- a/src/app/(admin)/atc24$rw/admin/components/ReviewForm.tsx +++ b/src/app/(admin)/atc24$rw/admin/components/ReviewForm.tsx @@ -24,6 +24,7 @@ import { toast } from "sonner"; import { Textarea } from "@/components/ui/textarea"; import { useDropzone } from "react-dropzone"; import Image from "next/image"; +import { REVIEWS_API } from "@/lib/constants"; interface ReviewFormProps { review?: Review; @@ -50,7 +51,7 @@ export const ReviewForm = ({ review }: ReviewFormProps) => { review: review?.review || "", rating: review?.rating || "5", user: review?.user || "", - active: review?.active || "false" + active: review?.active || "false", }, }); @@ -58,8 +59,16 @@ export const ReviewForm = ({ review }: ReviewFormProps) => { values: z.infer, token: string ) => { + const formData = new FormData(); + formData.append("review", values.review); + formData.append("rating", String(values.rating)); + formData.append("user", values.user); + formData.append("date", new Date().toISOString()); + formData.append("file", acceptedFiles[0]); + formData.append("active", JSON.parse(values.active || "false") ? "true" : "false"); + startTransition(() => { - createReview(values, acceptedFiles[0], token) + createReview(formData, token) .then((data) => { if (data.success) { toast.success(data.success); @@ -67,7 +76,7 @@ export const ReviewForm = ({ review }: ReviewFormProps) => { } if (data.error) { toast.error(data.error); - router.push("/atc24$rw"); + // router.push("/atc24$rw"); } }) .catch(() => toast.error("Ocurrió un error")); @@ -75,8 +84,18 @@ export const ReviewForm = ({ review }: ReviewFormProps) => { }; const handleUpdate = (review: Review, token: string) => { + const formData = new FormData(); + formData.append("id", review.id); + formData.append("review", review.review); + formData.append("rating", String(review.rating)); + formData.append("user", review.user); + formData.append("date", new Date().toISOString()); + formData.append("file", acceptedFiles[0] || new File([], "null", { + type: "image/png", + })); + formData.append("active", review.active ? "true" : "false"); startTransition(() => { - updateReview(review, token) + updateReview(formData, token) .then((data) => { if (data.success) { toast.success(data.success); @@ -84,7 +103,7 @@ export const ReviewForm = ({ review }: ReviewFormProps) => { } if (data.error) { toast.error(data.error); - router.push("/atc24$rw"); + // router.push("/atc24$rw"); } }) .catch(() => toast.error("Ocurrió un error")); @@ -159,7 +178,15 @@ export const ReviewForm = ({ review }: ReviewFormProps) => { className="w-full p-4 shadow-sm text-sm text-muted-foreground rounded-md border border-input bg-transparent flex flex-col items-center justify-center" > - {acceptedFiles[0] ? ( + {review?.image && !acceptedFiles[0] ? ( + Imagen del usuario + ) : acceptedFiles[0] ? ( { type="checkbox" id="show" className="w-5 h-5" + checked={JSON.parse(field.value || 'false') ? true : false} />