From 42519f3890717c5b598bdb0e4c7bfd812853d864 Mon Sep 17 00:00:00 2001
From: lajbel <71136486+lajbel@users.noreply.github.com>
Date: Thu, 31 Jul 2025 14:34:51 -0300
Subject: [PATCH 1/7] initial commit, start frontend
---
.vscode/settings.json | 3 +
package.json | 3 +-
src/app/page.tsx | 5 +
src/app/roast-my-setup/page.tsx | 16 ++
src/app/roast-my-setup/roaster/page.tsx | 5 +
src/roast-my-setup/components/error-badge.tsx | 22 +++
.../components/feedback-email.tsx | 28 ++++
.../components/feedback-form.tsx | 137 ++++++++++++++++++
src/roast-my-setup/components/flags.tsx | 109 ++++++++++++++
src/roast-my-setup/components/layout.tsx | 17 +++
src/roast-my-setup/components/pdf.tsx | 94 ++++++++++++
src/roast-my-setup/components/score.tsx | 66 +++++++++
src/roast-my-setup/components/skeleton.tsx | 15 ++
src/roast-my-setup/hooks/form-context.tsx | 35 +++++
src/roast-my-setup/pages/index.tsx | 67 +++++++++
src/roast-my-setup/pages/privacy.tsx | 65 +++++++++
src/roast-my-setup/pages/roaster.tsx | 36 +++++
src/roast-my-setup/types.ts | 5 +
src/roast-my-setup/utils.ts | 2 +
19 files changed, 729 insertions(+), 1 deletion(-)
create mode 100644 .vscode/settings.json
create mode 100644 src/app/roast-my-setup/page.tsx
create mode 100644 src/app/roast-my-setup/roaster/page.tsx
create mode 100644 src/roast-my-setup/components/error-badge.tsx
create mode 100644 src/roast-my-setup/components/feedback-email.tsx
create mode 100644 src/roast-my-setup/components/feedback-form.tsx
create mode 100644 src/roast-my-setup/components/flags.tsx
create mode 100644 src/roast-my-setup/components/layout.tsx
create mode 100644 src/roast-my-setup/components/pdf.tsx
create mode 100644 src/roast-my-setup/components/score.tsx
create mode 100644 src/roast-my-setup/components/skeleton.tsx
create mode 100644 src/roast-my-setup/hooks/form-context.tsx
create mode 100644 src/roast-my-setup/pages/index.tsx
create mode 100644 src/roast-my-setup/pages/privacy.tsx
create mode 100644 src/roast-my-setup/pages/roaster.tsx
create mode 100644 src/roast-my-setup/types.ts
create mode 100644 src/roast-my-setup/utils.ts
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..6a3b83c
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "javascript.preferences.importModuleSpecifierEnding": "minimal"
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index b3d8f04..d6828b1 100644
--- a/package.json
+++ b/package.json
@@ -85,5 +85,6 @@
"postcss": "^8",
"tailwindcss": "^4",
"typescript": "^5"
- }
+ },
+ "packageManager": "pnpm@10.8.1+sha512.c50088ba998c67b8ca8c99df8a5e02fd2ae2e2b29aaf238feaa9e124248d3f48f9fb6db2424949ff901cffbb5e0f0cc1ad6aedb602cd29450751d11c35023677"
}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 9413aa3..a7ffa31 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -67,6 +67,11 @@ const tools: {
description: "Test your speed and practice typing code.",
href: "https://wpm.silver.dev",
},
+ {
+ title: "Roast My Setup",
+ description: "Recíbi feedback de tu setup.",
+ href: "/roast-my-setup",
+ },
],
},
{
diff --git a/src/app/roast-my-setup/page.tsx b/src/app/roast-my-setup/page.tsx
new file mode 100644
index 0000000..330936f
--- /dev/null
+++ b/src/app/roast-my-setup/page.tsx
@@ -0,0 +1,16 @@
+import Home from "@/roast-my-setup/pages/index";
+import { Metadata } from "next";
+
+export const metadata: Metadata = {
+ title: "Roast My Setup",
+ description: "Submit your setup and get a roast for improvement.",
+ openGraph: {
+ title: "Roast My Setup • Open Silver",
+ description: "Show off your setup and get roasted for fun and improvement.",
+ type: "website",
+ },
+};
+
+export default function RoastMySetupPage() {
+ return ;
+}
diff --git a/src/app/roast-my-setup/roaster/page.tsx b/src/app/roast-my-setup/roaster/page.tsx
new file mode 100644
index 0000000..4615245
--- /dev/null
+++ b/src/app/roast-my-setup/roaster/page.tsx
@@ -0,0 +1,5 @@
+import Roaster from "@/roast-my-setup/pages/roaster";
+
+export default function BehavioralCheckerPage() {
+ return ;
+}
diff --git a/src/roast-my-setup/components/error-badge.tsx b/src/roast-my-setup/components/error-badge.tsx
new file mode 100644
index 0000000..184f771
--- /dev/null
+++ b/src/roast-my-setup/components/error-badge.tsx
@@ -0,0 +1,22 @@
+import { useEffect, useState } from "react";
+
+export default function ErrorBadge({ error }: { error: Error | null }) {
+ const [d, set] = useState(false);
+
+ function dismiss() {
+ set(true);
+ }
+
+ useEffect(() => {
+ set(false);
+ }, [error]);
+
+ return error && !d ? (
+
+
{error.message}
+
+ ×
+
+
+ ) : null;
+}
diff --git a/src/roast-my-setup/components/feedback-email.tsx b/src/roast-my-setup/components/feedback-email.tsx
new file mode 100644
index 0000000..24f3abd
--- /dev/null
+++ b/src/roast-my-setup/components/feedback-email.tsx
@@ -0,0 +1,28 @@
+import { FormState } from "@/resume-checker/types";
+
+export default function FeedbackEmail({
+ yellow_flags,
+ red_flags,
+ grade,
+ description,
+}: FormState & { description: string }) {
+ return (
+
+
Description:
+
{description}
+
Score: {grade}
+
Yellow flags:
+
+ {yellow_flags.map((flag) => (
+ {flag}
+ ))}
+
+
Red flags:
+
+ {red_flags.map((flag) => (
+ {flag}
+ ))}
+
+
+ );
+}
diff --git a/src/roast-my-setup/components/feedback-form.tsx b/src/roast-my-setup/components/feedback-form.tsx
new file mode 100644
index 0000000..24280f9
--- /dev/null
+++ b/src/roast-my-setup/components/feedback-form.tsx
@@ -0,0 +1,137 @@
+import { Button } from "@/components/ui/button";
+import { useFormState } from "@/resume-checker/hooks/form-context";
+import { FormState } from "@/resume-checker/types";
+import { useMutation } from "@tanstack/react-query";
+import { Dispatch, FormEvent, SetStateAction, useState } from "react";
+
+export default function FeedbackForm({
+ data,
+ isFeedbackFormOpen,
+ setFeedbackFormOpen,
+}: {
+ data: FormState;
+ isFeedbackFormOpen: boolean;
+ setFeedbackFormOpen: Dispatch>;
+}) {
+ const [formState] = useFormState();
+ const [hasConsented, setHasConsented] = useState(false);
+
+ const feedbackMutation = useMutation({
+ mutationKey: ["feedback"],
+ mutationFn: async ({ formData }) => {
+ if (!hasConsented)
+ throw new Error(
+ "Necesitamos tu consentimiento para poder enviar el feedback."
+ );
+ const res = await fetch("/api/feedback", {
+ method: "POST",
+ body: formData,
+ }).then((blob) => blob.json());
+
+ if (!res.success) {
+ throw new Error(res.message);
+ }
+ },
+ onSuccess: () => {
+ setFeedbackFormOpen(false);
+ },
+ });
+
+ function handleFeedbackSubmission(event: FormEvent) {
+ event.preventDefault();
+ const form = event.currentTarget;
+ if (!form || !(form instanceof HTMLFormElement)) return;
+
+ const file = formState.formData?.get("resume");
+ const formData = new FormData(form);
+
+ if (file) {
+ formData.set("resume", file);
+ }
+
+ feedbackMutation.mutate({ formData }, { onSuccess: form.reset });
+ }
+
+ function close() {
+ setFeedbackFormOpen(false);
+ }
+
+ return (
+
+ );
+}
diff --git a/src/roast-my-setup/components/flags.tsx b/src/roast-my-setup/components/flags.tsx
new file mode 100644
index 0000000..db638bd
--- /dev/null
+++ b/src/roast-my-setup/components/flags.tsx
@@ -0,0 +1,109 @@
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { FaFlag } from "react-icons/fa";
+import Markdown from "react-markdown";
+import { twMerge } from "tailwind-merge";
+
+function Flag({ color }: { color: string }) {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+
+export default function Flags({
+ flags,
+ color,
+ label,
+}: {
+ flags: Array;
+ color: string;
+ label: string;
+}) {
+ const getColorStyles = (color: string) => {
+ const colorMap: Record<
+ string,
+ { border: string; text: string; icon: string }
+ > = {
+ red: {
+ border: "border-red-500",
+ text: "text-red-500",
+ icon: "text-red-500",
+ },
+ yellow: {
+ border: "border-yellow-500",
+ text: "text-yellow-500",
+ icon: "text-yellow-500",
+ },
+ green: {
+ border: "border-green-500",
+ text: "text-green-500",
+ icon: "text-green-500",
+ },
+ };
+ return colorMap[color.toLowerCase()] || colorMap.green;
+ };
+
+ const { border, text, icon } = getColorStyles(color);
+
+ return (
+
+
+
+
+ {label} ({flags.length})
+
+
+
+
+ {flags.length > 0 ? (
+ flags.map((flag) => (
+
+ (
+
+ {children}
+
+ ),
+ }}
+ >
+ {flag}
+
+
+ ))
+ ) : (
+ No flags
+ )}
+
+
+
+ );
+}
diff --git a/src/roast-my-setup/components/layout.tsx b/src/roast-my-setup/components/layout.tsx
new file mode 100644
index 0000000..bf0f018
--- /dev/null
+++ b/src/roast-my-setup/components/layout.tsx
@@ -0,0 +1,17 @@
+import { Lato } from "next/font/google";
+import { type ReactNode } from "react";
+const lato = Lato({ subsets: ["latin"], weight: "400" });
+
+export default function Layout({ children }: { children: ReactNode }) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/src/roast-my-setup/components/pdf.tsx b/src/roast-my-setup/components/pdf.tsx
new file mode 100644
index 0000000..722626f
--- /dev/null
+++ b/src/roast-my-setup/components/pdf.tsx
@@ -0,0 +1,94 @@
+import { useFormState } from "@/resume-checker/hooks/form-context";
+import Image from "next/image";
+import * as pdfjsLib from "pdfjs-dist";
+import { useEffect, useState } from "react";
+
+pdfjsLib.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib.version}/build/pdf.worker.min.mjs`;
+
+function getUrlFromFormData(formData?: FormData) {
+ if (!formData) return;
+ const resume = formData.get("resume");
+ if (resume instanceof Blob) return URL.createObjectURL(resume);
+}
+
+function getUrlFromFormUrl(url?: string) {
+ if (!url) return;
+ if (url.startsWith("https")) return url;
+ return url.replace("public", "");
+}
+
+export default function PDF() {
+ const [formState] = useFormState();
+ const [showFallback, setShowFallback] = useState(false);
+ console.log(formState);
+ const url = formState.formData
+ ? getUrlFromFormData(formState.formData)
+ : getUrlFromFormUrl(formState.url);
+
+ return (
+ setShowFallback(true)}
+ onLoad={(object) => {
+ // free memory
+ URL.revokeObjectURL((object.target as HTMLObjectElement).data);
+ }}
+ width="100%"
+ height="100%"
+ >
+ {showFallback ? : null}
+
+ );
+}
+
+function ImageFallback({ url }: { url: string | undefined }) {
+ const [images, setImages] = useState>([]);
+
+ useEffect(() => {
+ async function handleConversion() {
+ if (!url) return;
+
+ try {
+ const pdf = await pdfjsLib.getDocument(url).promise;
+
+ const imgs = [];
+ for (let pageNumber = 0; pageNumber < pdf.numPages; pageNumber++) {
+ const page = await pdf.getPage(pageNumber + 1);
+ const viewport = page.getViewport({ scale: 2 });
+ const canvas = document.createElement("canvas");
+ const ctx = canvas.getContext("2d");
+ canvas.width = viewport.width;
+ canvas.height = viewport.height;
+
+ await page.render({ canvasContext: ctx!, viewport }).promise;
+
+ const imgUrl = canvas.toDataURL("image/jpeg");
+ imgs.push(imgUrl);
+ }
+
+ setImages(imgs);
+ } catch {
+ console.log("there was an error coverting pdf to jpeg");
+ }
+ }
+
+ handleConversion();
+ }, [url]);
+
+ return (
+
+ {images.map((img, idx) => (
+
+ ))}
+
+ );
+}
diff --git a/src/roast-my-setup/components/score.tsx b/src/roast-my-setup/components/score.tsx
new file mode 100644
index 0000000..27b7fc6
--- /dev/null
+++ b/src/roast-my-setup/components/score.tsx
@@ -0,0 +1,66 @@
+import { PreppingData } from "@/lib/utils";
+import { useEffect, useState } from "react";
+
+const letterColors = {
+ S: "bg-gradient-to-tr from-pink-500 to-blue-500",
+ A: "bg-green-500/50",
+ B: "bg-green-400/50",
+ C: "bg-yellow-400/50",
+ " ": "bg-white/30",
+} as const;
+
+const letterDescriptions = {
+ S: "El CV sigue todas las recomendaciones de Silver en formato y contenido y va a tener resultados optimos en procesos de entrevista",
+ A: "El CV tiene pocos desperfectos y los recruiters van a poder leer tu perfil de manera efectiva. De todas maneras mejorarlo te siempre ayuda",
+ B: "El CV tiene desperfectos que perjudican las chances de conseguir entrevistas y dar buenas impresiones en el equipo de contratación. Recomendamos que mejores los items demarcados",
+ C: "El CV va a ser descartado rapidamente en las screenings. Recomendamos que rehagas completamente desde 0 el curriculum usando nuestro template",
+ " ": "",
+} as const;
+
+type Letter = keyof typeof letterColors;
+
+const letterKeys = Object.keys(letterColors) as Array;
+
+function loadingStyles(l: string) {
+ if (l !== " ") return "";
+
+ return "animate-pulse";
+}
+
+export default function Score({ letter }: { letter?: string }) {
+ const [idx, setIdx] = useState(letterKeys.length - 1);
+
+ useEffect(() => {
+ const index = letterKeys.indexOf(letter as Letter);
+ if (index !== -1) {
+ PreppingData.setToolData("resume-checker", letter as string);
+ setIdx(index);
+ }
+ }, [letter]);
+
+ return (
+
+
+
+ {letterKeys.map((l) => (
+
+ {l}
+
+ ))}
+
+
+
+ {letter ?
{letterDescriptions[letter as Letter]}
: null}
+
+ );
+}
diff --git a/src/roast-my-setup/components/skeleton.tsx b/src/roast-my-setup/components/skeleton.tsx
new file mode 100644
index 0000000..572ad07
--- /dev/null
+++ b/src/roast-my-setup/components/skeleton.tsx
@@ -0,0 +1,15 @@
+export default function Skeleton() {
+ return (
+
+ );
+}
diff --git a/src/roast-my-setup/hooks/form-context.tsx b/src/roast-my-setup/hooks/form-context.tsx
new file mode 100644
index 0000000..3711a94
--- /dev/null
+++ b/src/roast-my-setup/hooks/form-context.tsx
@@ -0,0 +1,35 @@
+"use client";
+
+import {
+ createContext,
+ Dispatch,
+ ReactNode,
+ SetStateAction,
+ useContext,
+ useState,
+} from "react";
+
+type State = { url?: string; formData?: FormData };
+const FormContext = createContext<[State, Dispatch>]>([
+ {},
+ () => {},
+]);
+
+export function FormProvider({ children }: { children: ReactNode }) {
+ const [state, setState] = useState({});
+ return (
+
+ {children}
+
+ );
+}
+
+export function useFormState() {
+ const ctx = useContext(FormContext);
+
+ if (!ctx) {
+ throw new Error("useFormState must be used within a FormProvider");
+ }
+
+ return ctx;
+}
diff --git a/src/roast-my-setup/pages/index.tsx b/src/roast-my-setup/pages/index.tsx
new file mode 100644
index 0000000..fa65762
--- /dev/null
+++ b/src/roast-my-setup/pages/index.tsx
@@ -0,0 +1,67 @@
+"use client";
+
+import Description from "@/components/description";
+import Heading from "@/components/heading";
+import Section from "@/components/section";
+import Spacer from "@/components/spacer";
+import { Button } from "@/components/ui/button";
+import ErrorBadge from "@/resume-checker/components/error-badge";
+import Link from "next/link";
+import { useRouter } from "next/navigation";
+import { useEffect, useState } from "react";
+
+function usePasteEvent(pasteListener: (event: ClipboardEvent) => void) {
+ useEffect(() => {
+ document.addEventListener("paste", pasteListener);
+
+ return () => {
+ document.removeEventListener("paste", pasteListener);
+ };
+ }, [pasteListener]);
+}
+
+export default function Home() {
+ const router = useRouter();
+ const [error, setError] = useState(null);
+
+ const requestCameraAndMicrophone = () => {
+ navigator.mediaDevices
+ .getUserMedia({ video: true, audio: true })
+ .then((stream) => {
+ router.push("/roast-my-setup/roaster");
+ })
+ .catch((err) => {
+ setError(new Error("Access to microphone not granted."));
+ });
+ };
+
+ return (
+
+
+
+ Roast My{" "}
+ Setup
+
+
+ Get feedback from your setup.
+ Como te ven los demas?
+
+
+
+ Roast Me
+
+
+
+
+
+
+ Política de Privacidad
+
+
+
+ );
+}
diff --git a/src/roast-my-setup/pages/privacy.tsx b/src/roast-my-setup/pages/privacy.tsx
new file mode 100644
index 0000000..c5d4ded
--- /dev/null
+++ b/src/roast-my-setup/pages/privacy.tsx
@@ -0,0 +1,65 @@
+import Heading from "@/components/heading";
+import Section from "@/components/section";
+import Spacer from "@/components/spacer";
+
+export default function Privacy() {
+ return (
+
+ Política de Privacidad
+
+
+ Gracias por utilizar Roast my Setup. Su privacidad es muy importante
+ para nosotros, y estamos comprometidos a ser transparentes sobre cómo
+ manejamos sus datos. A continuación, encontrará los detalles sobre
+ nuestras prácticas de privacidad:
+
+
+
+
+ No almacenamos currículums ni información personal
+
+
+ La herramienta Roast my Setup no almacena los videos ni ninguna
+ información contenida en ellos en nuestros servidores ni en ningún
+ otro lugar. Una vez que su documento es procesado, todos los datos
+ se eliminan de forma inmediata.
+
+
+
+
+ Uso de tecnología de inteligencia artificial
+
+
+ Para el análisis y retroalimentación del contenido, la herramienta
+ utiliza Gemini AI. Este proceso se realiza de manera segura y no se
+ retiene ningún dato después del análisis.
+
+
+
+ Seguridad de los datos
+
+ Nos aseguramos de que sus datos sean procesados en un entorno
+ seguro. Dado que no almacenamos información, no existe riesgo de
+ acceso no autorizado ni de uso indebido de sus datos.
+
+
+
+ Servicios de terceros
+
+ La herramienta Roast My Setup utiliza [Redacted] para el análisis
+ del contenido y la generación de feedback. [Redacted] opera bajo sus
+ propias políticas de privacidad y seguridad, diseñadas para manejar
+ sus datos de manera responsable.
+
+
+
+ Su consentimiento
+
+ Al utilizar la herramienta Roast my Setup, usted acepta los términos
+ descritos en esta Política de Privacidad.
+
+
+
+
+ );
+}
diff --git a/src/roast-my-setup/pages/roaster.tsx b/src/roast-my-setup/pages/roaster.tsx
new file mode 100644
index 0000000..08d1ec1
--- /dev/null
+++ b/src/roast-my-setup/pages/roaster.tsx
@@ -0,0 +1,36 @@
+"use client";
+
+import { Card } from "@/components/ui/card";
+import { useEffect, useRef } from "react";
+
+export default function Roaster() {
+ const videoRef = useRef(null);
+
+ useEffect(() => {
+ navigator.mediaDevices
+ .getUserMedia({ video: true })
+ .then((stream) => {
+ if (videoRef.current) {
+ videoRef.current.srcObject = stream;
+ }
+ })
+ .catch((err) => {
+ console.error("Error al acceder a la cámara:", err);
+ });
+ }, []);
+
+ return (
+
+
+
+
+
Say "Roast my Setup"
+
+ );
+}
diff --git a/src/roast-my-setup/types.ts b/src/roast-my-setup/types.ts
new file mode 100644
index 0000000..75db505
--- /dev/null
+++ b/src/roast-my-setup/types.ts
@@ -0,0 +1,5 @@
+export type FormState = {
+ grade: string;
+ red_flags: Array;
+ yellow_flags: Array;
+};
diff --git a/src/roast-my-setup/utils.ts b/src/roast-my-setup/utils.ts
new file mode 100644
index 0000000..1f2894b
--- /dev/null
+++ b/src/roast-my-setup/utils.ts
@@ -0,0 +1,2 @@
+const TYPST_TEMPLATE_VERSION = "1.0.2";
+export const TYPST_TEMPLATE_URL = `https://typst.app/app?template=silver-dev-cv&version=${TYPST_TEMPLATE_VERSION}`;
From 3f64952441683258404c3ea26b8c89712c362d97 Mon Sep 17 00:00:00 2001
From: lajbel <71136486+lajbel@users.noreply.github.com>
Date: Thu, 31 Jul 2025 14:37:16 -0300
Subject: [PATCH 2/7] remove own configurations
---
.gitignore | 3 +++
.vscode/settings.json | 3 ---
package.json | 3 +--
3 files changed, 4 insertions(+), 5 deletions(-)
delete mode 100644 .vscode/settings.json
diff --git a/.gitignore b/.gitignore
index 5ef6a52..ce83b01 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,3 +39,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+# vscode
+.vscode/settings.json
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
deleted file mode 100644
index 6a3b83c..0000000
--- a/.vscode/settings.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "javascript.preferences.importModuleSpecifierEnding": "minimal"
-}
\ No newline at end of file
diff --git a/package.json b/package.json
index d6828b1..b3d8f04 100644
--- a/package.json
+++ b/package.json
@@ -85,6 +85,5 @@
"postcss": "^8",
"tailwindcss": "^4",
"typescript": "^5"
- },
- "packageManager": "pnpm@10.8.1+sha512.c50088ba998c67b8ca8c99df8a5e02fd2ae2e2b29aaf238feaa9e124248d3f48f9fb6db2424949ff901cffbb5e0f0cc1ad6aedb602cd29450751d11c35023677"
+ }
}
From 14400224da38bdd509c82d3428d8a34ccfb921e9 Mon Sep 17 00:00:00 2001
From: lajbel <71136486+lajbel@users.noreply.github.com>
Date: Thu, 31 Jul 2025 14:39:12 -0300
Subject: [PATCH 3/7] clean unused
---
.../components/feedback-email.tsx | 28 ----
.../components/feedback-form.tsx | 137 ------------------
src/roast-my-setup/components/flags.tsx | 109 --------------
src/roast-my-setup/components/layout.tsx | 17 ---
src/roast-my-setup/components/pdf.tsx | 94 ------------
src/roast-my-setup/components/score.tsx | 66 ---------
src/roast-my-setup/components/skeleton.tsx | 15 --
src/roast-my-setup/hooks/form-context.tsx | 35 -----
src/roast-my-setup/pages/index.tsx | 12 +-
src/roast-my-setup/types.ts | 5 -
src/roast-my-setup/utils.ts | 2 -
11 files changed, 1 insertion(+), 519 deletions(-)
delete mode 100644 src/roast-my-setup/components/feedback-email.tsx
delete mode 100644 src/roast-my-setup/components/feedback-form.tsx
delete mode 100644 src/roast-my-setup/components/flags.tsx
delete mode 100644 src/roast-my-setup/components/layout.tsx
delete mode 100644 src/roast-my-setup/components/pdf.tsx
delete mode 100644 src/roast-my-setup/components/score.tsx
delete mode 100644 src/roast-my-setup/components/skeleton.tsx
delete mode 100644 src/roast-my-setup/hooks/form-context.tsx
delete mode 100644 src/roast-my-setup/types.ts
delete mode 100644 src/roast-my-setup/utils.ts
diff --git a/src/roast-my-setup/components/feedback-email.tsx b/src/roast-my-setup/components/feedback-email.tsx
deleted file mode 100644
index 24f3abd..0000000
--- a/src/roast-my-setup/components/feedback-email.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { FormState } from "@/resume-checker/types";
-
-export default function FeedbackEmail({
- yellow_flags,
- red_flags,
- grade,
- description,
-}: FormState & { description: string }) {
- return (
-
-
Description:
-
{description}
-
Score: {grade}
-
Yellow flags:
-
- {yellow_flags.map((flag) => (
- {flag}
- ))}
-
-
Red flags:
-
- {red_flags.map((flag) => (
- {flag}
- ))}
-
-
- );
-}
diff --git a/src/roast-my-setup/components/feedback-form.tsx b/src/roast-my-setup/components/feedback-form.tsx
deleted file mode 100644
index 24280f9..0000000
--- a/src/roast-my-setup/components/feedback-form.tsx
+++ /dev/null
@@ -1,137 +0,0 @@
-import { Button } from "@/components/ui/button";
-import { useFormState } from "@/resume-checker/hooks/form-context";
-import { FormState } from "@/resume-checker/types";
-import { useMutation } from "@tanstack/react-query";
-import { Dispatch, FormEvent, SetStateAction, useState } from "react";
-
-export default function FeedbackForm({
- data,
- isFeedbackFormOpen,
- setFeedbackFormOpen,
-}: {
- data: FormState;
- isFeedbackFormOpen: boolean;
- setFeedbackFormOpen: Dispatch>;
-}) {
- const [formState] = useFormState();
- const [hasConsented, setHasConsented] = useState(false);
-
- const feedbackMutation = useMutation({
- mutationKey: ["feedback"],
- mutationFn: async ({ formData }) => {
- if (!hasConsented)
- throw new Error(
- "Necesitamos tu consentimiento para poder enviar el feedback."
- );
- const res = await fetch("/api/feedback", {
- method: "POST",
- body: formData,
- }).then((blob) => blob.json());
-
- if (!res.success) {
- throw new Error(res.message);
- }
- },
- onSuccess: () => {
- setFeedbackFormOpen(false);
- },
- });
-
- function handleFeedbackSubmission(event: FormEvent) {
- event.preventDefault();
- const form = event.currentTarget;
- if (!form || !(form instanceof HTMLFormElement)) return;
-
- const file = formState.formData?.get("resume");
- const formData = new FormData(form);
-
- if (file) {
- formData.set("resume", file);
- }
-
- feedbackMutation.mutate({ formData }, { onSuccess: form.reset });
- }
-
- function close() {
- setFeedbackFormOpen(false);
- }
-
- return (
-
- e.stopPropagation()}
- className={`fixed transition-transform left-0 top-0 h-full w-full sm:h-max sm:rounded-lg sm:-translate-x-1/2 sm:-translate-y-1/2 sm:top-1/2 sm:left-1/2 bg-background p-10 shadow-lg flex justify-between flex-col sm:max-w-xl`}
- >
-
-
- ×
-
- {feedbackMutation.error ? (
-
- {feedbackMutation.error.message}
-
- ) : null}
-
-
-
-
-
-
-
- Gracias por tu feedback
-
-
-
-
-
- Acepto que mi CV sea compartido con el equipo de Resume Checker.
-
- setHasConsented(e.target.checked)}
- />
-
-
- Enviar Feedback
-
-
-
-
- );
-}
diff --git a/src/roast-my-setup/components/flags.tsx b/src/roast-my-setup/components/flags.tsx
deleted file mode 100644
index db638bd..0000000
--- a/src/roast-my-setup/components/flags.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
-import { FaFlag } from "react-icons/fa";
-import Markdown from "react-markdown";
-import { twMerge } from "tailwind-merge";
-
-function Flag({ color }: { color: string }) {
- return (
-
-
-
-
-
-
-
-
- );
-}
-
-export default function Flags({
- flags,
- color,
- label,
-}: {
- flags: Array;
- color: string;
- label: string;
-}) {
- const getColorStyles = (color: string) => {
- const colorMap: Record<
- string,
- { border: string; text: string; icon: string }
- > = {
- red: {
- border: "border-red-500",
- text: "text-red-500",
- icon: "text-red-500",
- },
- yellow: {
- border: "border-yellow-500",
- text: "text-yellow-500",
- icon: "text-yellow-500",
- },
- green: {
- border: "border-green-500",
- text: "text-green-500",
- icon: "text-green-500",
- },
- };
- return colorMap[color.toLowerCase()] || colorMap.green;
- };
-
- const { border, text, icon } = getColorStyles(color);
-
- return (
-
-
-
-
- {label} ({flags.length})
-
-
-
-
- {flags.length > 0 ? (
- flags.map((flag) => (
-
- (
-
- {children}
-
- ),
- }}
- >
- {flag}
-
-
- ))
- ) : (
- No flags
- )}
-
-
-
- );
-}
diff --git a/src/roast-my-setup/components/layout.tsx b/src/roast-my-setup/components/layout.tsx
deleted file mode 100644
index bf0f018..0000000
--- a/src/roast-my-setup/components/layout.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Lato } from "next/font/google";
-import { type ReactNode } from "react";
-const lato = Lato({ subsets: ["latin"], weight: "400" });
-
-export default function Layout({ children }: { children: ReactNode }) {
- return (
-
-
- {children}
-
-
- );
-}
diff --git a/src/roast-my-setup/components/pdf.tsx b/src/roast-my-setup/components/pdf.tsx
deleted file mode 100644
index 722626f..0000000
--- a/src/roast-my-setup/components/pdf.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-import { useFormState } from "@/resume-checker/hooks/form-context";
-import Image from "next/image";
-import * as pdfjsLib from "pdfjs-dist";
-import { useEffect, useState } from "react";
-
-pdfjsLib.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib.version}/build/pdf.worker.min.mjs`;
-
-function getUrlFromFormData(formData?: FormData) {
- if (!formData) return;
- const resume = formData.get("resume");
- if (resume instanceof Blob) return URL.createObjectURL(resume);
-}
-
-function getUrlFromFormUrl(url?: string) {
- if (!url) return;
- if (url.startsWith("https")) return url;
- return url.replace("public", "");
-}
-
-export default function PDF() {
- const [formState] = useFormState();
- const [showFallback, setShowFallback] = useState(false);
- console.log(formState);
- const url = formState.formData
- ? getUrlFromFormData(formState.formData)
- : getUrlFromFormUrl(formState.url);
-
- return (
- setShowFallback(true)}
- onLoad={(object) => {
- // free memory
- URL.revokeObjectURL((object.target as HTMLObjectElement).data);
- }}
- width="100%"
- height="100%"
- >
- {showFallback ? : null}
-
- );
-}
-
-function ImageFallback({ url }: { url: string | undefined }) {
- const [images, setImages] = useState>([]);
-
- useEffect(() => {
- async function handleConversion() {
- if (!url) return;
-
- try {
- const pdf = await pdfjsLib.getDocument(url).promise;
-
- const imgs = [];
- for (let pageNumber = 0; pageNumber < pdf.numPages; pageNumber++) {
- const page = await pdf.getPage(pageNumber + 1);
- const viewport = page.getViewport({ scale: 2 });
- const canvas = document.createElement("canvas");
- const ctx = canvas.getContext("2d");
- canvas.width = viewport.width;
- canvas.height = viewport.height;
-
- await page.render({ canvasContext: ctx!, viewport }).promise;
-
- const imgUrl = canvas.toDataURL("image/jpeg");
- imgs.push(imgUrl);
- }
-
- setImages(imgs);
- } catch {
- console.log("there was an error coverting pdf to jpeg");
- }
- }
-
- handleConversion();
- }, [url]);
-
- return (
-
- {images.map((img, idx) => (
-
- ))}
-
- );
-}
diff --git a/src/roast-my-setup/components/score.tsx b/src/roast-my-setup/components/score.tsx
deleted file mode 100644
index 27b7fc6..0000000
--- a/src/roast-my-setup/components/score.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import { PreppingData } from "@/lib/utils";
-import { useEffect, useState } from "react";
-
-const letterColors = {
- S: "bg-gradient-to-tr from-pink-500 to-blue-500",
- A: "bg-green-500/50",
- B: "bg-green-400/50",
- C: "bg-yellow-400/50",
- " ": "bg-white/30",
-} as const;
-
-const letterDescriptions = {
- S: "El CV sigue todas las recomendaciones de Silver en formato y contenido y va a tener resultados optimos en procesos de entrevista",
- A: "El CV tiene pocos desperfectos y los recruiters van a poder leer tu perfil de manera efectiva. De todas maneras mejorarlo te siempre ayuda",
- B: "El CV tiene desperfectos que perjudican las chances de conseguir entrevistas y dar buenas impresiones en el equipo de contratación. Recomendamos que mejores los items demarcados",
- C: "El CV va a ser descartado rapidamente en las screenings. Recomendamos que rehagas completamente desde 0 el curriculum usando nuestro template",
- " ": "",
-} as const;
-
-type Letter = keyof typeof letterColors;
-
-const letterKeys = Object.keys(letterColors) as Array;
-
-function loadingStyles(l: string) {
- if (l !== " ") return "";
-
- return "animate-pulse";
-}
-
-export default function Score({ letter }: { letter?: string }) {
- const [idx, setIdx] = useState(letterKeys.length - 1);
-
- useEffect(() => {
- const index = letterKeys.indexOf(letter as Letter);
- if (index !== -1) {
- PreppingData.setToolData("resume-checker", letter as string);
- setIdx(index);
- }
- }, [letter]);
-
- return (
-
-
-
- {letterKeys.map((l) => (
-
- {l}
-
- ))}
-
-
-
- {letter ?
{letterDescriptions[letter as Letter]}
: null}
-
- );
-}
diff --git a/src/roast-my-setup/components/skeleton.tsx b/src/roast-my-setup/components/skeleton.tsx
deleted file mode 100644
index 572ad07..0000000
--- a/src/roast-my-setup/components/skeleton.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-export default function Skeleton() {
- return (
-
- );
-}
diff --git a/src/roast-my-setup/hooks/form-context.tsx b/src/roast-my-setup/hooks/form-context.tsx
deleted file mode 100644
index 3711a94..0000000
--- a/src/roast-my-setup/hooks/form-context.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-"use client";
-
-import {
- createContext,
- Dispatch,
- ReactNode,
- SetStateAction,
- useContext,
- useState,
-} from "react";
-
-type State = { url?: string; formData?: FormData };
-const FormContext = createContext<[State, Dispatch>]>([
- {},
- () => {},
-]);
-
-export function FormProvider({ children }: { children: ReactNode }) {
- const [state, setState] = useState({});
- return (
-
- {children}
-
- );
-}
-
-export function useFormState() {
- const ctx = useContext(FormContext);
-
- if (!ctx) {
- throw new Error("useFormState must be used within a FormProvider");
- }
-
- return ctx;
-}
diff --git a/src/roast-my-setup/pages/index.tsx b/src/roast-my-setup/pages/index.tsx
index fa65762..3a077b0 100644
--- a/src/roast-my-setup/pages/index.tsx
+++ b/src/roast-my-setup/pages/index.tsx
@@ -8,17 +8,7 @@ import { Button } from "@/components/ui/button";
import ErrorBadge from "@/resume-checker/components/error-badge";
import Link from "next/link";
import { useRouter } from "next/navigation";
-import { useEffect, useState } from "react";
-
-function usePasteEvent(pasteListener: (event: ClipboardEvent) => void) {
- useEffect(() => {
- document.addEventListener("paste", pasteListener);
-
- return () => {
- document.removeEventListener("paste", pasteListener);
- };
- }, [pasteListener]);
-}
+import { useState } from "react";
export default function Home() {
const router = useRouter();
diff --git a/src/roast-my-setup/types.ts b/src/roast-my-setup/types.ts
deleted file mode 100644
index 75db505..0000000
--- a/src/roast-my-setup/types.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export type FormState = {
- grade: string;
- red_flags: Array;
- yellow_flags: Array;
-};
diff --git a/src/roast-my-setup/utils.ts b/src/roast-my-setup/utils.ts
deleted file mode 100644
index 1f2894b..0000000
--- a/src/roast-my-setup/utils.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-const TYPST_TEMPLATE_VERSION = "1.0.2";
-export const TYPST_TEMPLATE_URL = `https://typst.app/app?template=silver-dev-cv&version=${TYPST_TEMPLATE_VERSION}`;
From 8bc23df112e24953cff4be790b84cf16a1f9e4eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Pujia?=
Date: Thu, 31 Jul 2025 16:43:05 -0300
Subject: [PATCH 4/7] fix: privacy policy link
---
src/roast-my-setup/pages/index.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/roast-my-setup/pages/index.tsx b/src/roast-my-setup/pages/index.tsx
index 3a077b0..62b4293 100644
--- a/src/roast-my-setup/pages/index.tsx
+++ b/src/roast-my-setup/pages/index.tsx
@@ -48,7 +48,7 @@ export default function Home() {
-
+
Política de Privacidad
From 650d7327e685fd6054283e1c171bfd907948fdea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Pujia?=
Date: Thu, 31 Jul 2025 16:45:06 -0300
Subject: [PATCH 5/7] fix: move privacy page to app router
---
.../pages/privacy.tsx => app/roast-my-setup/privacy/page.tsx} | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
rename src/{roast-my-setup/pages/privacy.tsx => app/roast-my-setup/privacy/page.tsx} (95%)
diff --git a/src/roast-my-setup/pages/privacy.tsx b/src/app/roast-my-setup/privacy/page.tsx
similarity index 95%
rename from src/roast-my-setup/pages/privacy.tsx
rename to src/app/roast-my-setup/privacy/page.tsx
index c5d4ded..b62234b 100644
--- a/src/roast-my-setup/pages/privacy.tsx
+++ b/src/app/roast-my-setup/privacy/page.tsx
@@ -2,7 +2,7 @@ import Heading from "@/components/heading";
import Section from "@/components/section";
import Spacer from "@/components/spacer";
-export default function Privacy() {
+export default function Page() {
return (
Política de Privacidad
@@ -16,7 +16,7 @@ export default function Privacy() {
- No almacenamos currículums ni información personal
+ No almacenamos currículums {/* FIXME! */}ni información personal
La herramienta Roast my Setup no almacena los videos ni ninguna
From 4ca132dd3e6b8ed49110c53fed04ea88680bfb8f Mon Sep 17 00:00:00 2001
From: lajbel <71136486+lajbel@users.noreply.github.com>
Date: Mon, 4 Aug 2025 09:01:48 -0300
Subject: [PATCH 6/7] chore: move roaster files to app router
---
src/app/roast-my-setup/page.tsx | 58 +++++++++++++++++++++++--
src/app/roast-my-setup/privacy/page.tsx | 2 +-
src/app/roast-my-setup/roaster/page.tsx | 37 ++++++++++++++--
src/roast-my-setup/pages/index.tsx | 57 ------------------------
src/roast-my-setup/pages/roaster.tsx | 36 ---------------
5 files changed, 90 insertions(+), 100 deletions(-)
delete mode 100644 src/roast-my-setup/pages/index.tsx
delete mode 100644 src/roast-my-setup/pages/roaster.tsx
diff --git a/src/app/roast-my-setup/page.tsx b/src/app/roast-my-setup/page.tsx
index 330936f..8fb2fa3 100644
--- a/src/app/roast-my-setup/page.tsx
+++ b/src/app/roast-my-setup/page.tsx
@@ -1,5 +1,15 @@
-import Home from "@/roast-my-setup/pages/index";
+"use client";
+
import { Metadata } from "next";
+import Description from "@/components/description";
+import Heading from "@/components/heading";
+import Section from "@/components/section";
+import Spacer from "@/components/spacer";
+import { Button } from "@/components/ui/button";
+import ErrorBadge from "@/resume-checker/components/error-badge";
+import Link from "next/link";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
export const metadata: Metadata = {
title: "Roast My Setup",
@@ -11,6 +21,48 @@ export const metadata: Metadata = {
},
};
-export default function RoastMySetupPage() {
- return ;
+export default function Home() {
+ const router = useRouter();
+ const [error, setError] = useState(null);
+
+ const requestCameraAndMicrophone = () => {
+ navigator.mediaDevices
+ .getUserMedia({ video: true, audio: true })
+ .then((stream) => {
+ router.push("/roast-my-setup/roaster");
+ })
+ .catch((err) => {
+ setError(new Error("Access to microphone not granted."));
+ });
+ };
+
+ return (
+
+
+
+ Roast My{" "}
+ Setup
+
+
+ Get feedback from your setup.
+ Como te ven los demas?
+
+
+
+ Roast Me
+
+
+
+
+
+
+ Política de Privacidad
+
+
+
+ );
}
diff --git a/src/app/roast-my-setup/privacy/page.tsx b/src/app/roast-my-setup/privacy/page.tsx
index b62234b..e3db649 100644
--- a/src/app/roast-my-setup/privacy/page.tsx
+++ b/src/app/roast-my-setup/privacy/page.tsx
@@ -16,7 +16,7 @@ export default function Page() {
- No almacenamos currículums {/* FIXME! */}ni información personal
+ No almacenamos grabaciónes, audio ni información personal
La herramienta Roast my Setup no almacena los videos ni ninguna
diff --git a/src/app/roast-my-setup/roaster/page.tsx b/src/app/roast-my-setup/roaster/page.tsx
index 4615245..08d1ec1 100644
--- a/src/app/roast-my-setup/roaster/page.tsx
+++ b/src/app/roast-my-setup/roaster/page.tsx
@@ -1,5 +1,36 @@
-import Roaster from "@/roast-my-setup/pages/roaster";
+"use client";
-export default function BehavioralCheckerPage() {
- return ;
+import { Card } from "@/components/ui/card";
+import { useEffect, useRef } from "react";
+
+export default function Roaster() {
+ const videoRef = useRef(null);
+
+ useEffect(() => {
+ navigator.mediaDevices
+ .getUserMedia({ video: true })
+ .then((stream) => {
+ if (videoRef.current) {
+ videoRef.current.srcObject = stream;
+ }
+ })
+ .catch((err) => {
+ console.error("Error al acceder a la cámara:", err);
+ });
+ }, []);
+
+ return (
+
+
+
+
+
Say "Roast my Setup"
+
+ );
}
diff --git a/src/roast-my-setup/pages/index.tsx b/src/roast-my-setup/pages/index.tsx
deleted file mode 100644
index 62b4293..0000000
--- a/src/roast-my-setup/pages/index.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-"use client";
-
-import Description from "@/components/description";
-import Heading from "@/components/heading";
-import Section from "@/components/section";
-import Spacer from "@/components/spacer";
-import { Button } from "@/components/ui/button";
-import ErrorBadge from "@/resume-checker/components/error-badge";
-import Link from "next/link";
-import { useRouter } from "next/navigation";
-import { useState } from "react";
-
-export default function Home() {
- const router = useRouter();
- const [error, setError] = useState(null);
-
- const requestCameraAndMicrophone = () => {
- navigator.mediaDevices
- .getUserMedia({ video: true, audio: true })
- .then((stream) => {
- router.push("/roast-my-setup/roaster");
- })
- .catch((err) => {
- setError(new Error("Access to microphone not granted."));
- });
- };
-
- return (
-
-
-
- Roast My{" "}
- Setup
-
-
- Get feedback from your setup.
- Como te ven los demas?
-
-
-
- Roast Me
-
-
-
-
-
-
- Política de Privacidad
-
-
-
- );
-}
diff --git a/src/roast-my-setup/pages/roaster.tsx b/src/roast-my-setup/pages/roaster.tsx
deleted file mode 100644
index 08d1ec1..0000000
--- a/src/roast-my-setup/pages/roaster.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-"use client";
-
-import { Card } from "@/components/ui/card";
-import { useEffect, useRef } from "react";
-
-export default function Roaster() {
- const videoRef = useRef(null);
-
- useEffect(() => {
- navigator.mediaDevices
- .getUserMedia({ video: true })
- .then((stream) => {
- if (videoRef.current) {
- videoRef.current.srcObject = stream;
- }
- })
- .catch((err) => {
- console.error("Error al acceder a la cámara:", err);
- });
- }, []);
-
- return (
-
-
-
-
-
Say "Roast my Setup"
-
- );
-}
From 4fc47d67de2afe11ffd0160db592305a3dcb187a Mon Sep 17 00:00:00 2001
From: lajbel <71136486+lajbel@users.noreply.github.com>
Date: Mon, 4 Aug 2025 09:03:36 -0300
Subject: [PATCH 7/7] chore: move error-badge.tsx to src/components/ui
---
src/app/roast-my-setup/page.tsx | 2 +-
.../ui}/error-badge.tsx | 0
src/resume-checker/pages/index.tsx | 2 +-
src/roast-my-setup/components/error-badge.tsx | 22 -------------------
4 files changed, 2 insertions(+), 24 deletions(-)
rename src/{resume-checker/components => components/ui}/error-badge.tsx (100%)
delete mode 100644 src/roast-my-setup/components/error-badge.tsx
diff --git a/src/app/roast-my-setup/page.tsx b/src/app/roast-my-setup/page.tsx
index 8fb2fa3..fbb6f9d 100644
--- a/src/app/roast-my-setup/page.tsx
+++ b/src/app/roast-my-setup/page.tsx
@@ -6,7 +6,7 @@ import Heading from "@/components/heading";
import Section from "@/components/section";
import Spacer from "@/components/spacer";
import { Button } from "@/components/ui/button";
-import ErrorBadge from "@/resume-checker/components/error-badge";
+import ErrorBadge from "@/components/ui/error-badge";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
diff --git a/src/resume-checker/components/error-badge.tsx b/src/components/ui/error-badge.tsx
similarity index 100%
rename from src/resume-checker/components/error-badge.tsx
rename to src/components/ui/error-badge.tsx
diff --git a/src/resume-checker/pages/index.tsx b/src/resume-checker/pages/index.tsx
index cd2081a..5d8da7f 100644
--- a/src/resume-checker/pages/index.tsx
+++ b/src/resume-checker/pages/index.tsx
@@ -6,7 +6,7 @@ import Heading from "@/components/heading";
import Section from "@/components/section";
import Spacer, { spaceSizes } from "@/components/spacer";
import { Button } from "@/components/ui/button";
-import ErrorBadge from "@/resume-checker/components/error-badge";
+import ErrorBadge from "@/components/ui/error-badge";
import { useFormState } from "@/resume-checker/hooks/form-context";
import { useMutationState } from "@tanstack/react-query";
import Link from "next/link";
diff --git a/src/roast-my-setup/components/error-badge.tsx b/src/roast-my-setup/components/error-badge.tsx
deleted file mode 100644
index 184f771..0000000
--- a/src/roast-my-setup/components/error-badge.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { useEffect, useState } from "react";
-
-export default function ErrorBadge({ error }: { error: Error | null }) {
- const [d, set] = useState(false);
-
- function dismiss() {
- set(true);
- }
-
- useEffect(() => {
- set(false);
- }, [error]);
-
- return error && !d ? (
-
-
{error.message}
-
- ×
-
-
- ) : null;
-}