From b79b812c442c9d4cfdeca06ae6c106549954d12d Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 09:37:46 +0800 Subject: [PATCH 01/36] change handleReset order --- components/core/code-area-actions/submit-button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/core/code-area-actions/submit-button.tsx b/components/core/code-area-actions/submit-button.tsx index ed0354c..b33e4f0 100644 --- a/components/core/code-area-actions/submit-button.tsx +++ b/components/core/code-area-actions/submit-button.tsx @@ -82,8 +82,8 @@ const SubmitButton = () => { }; const handleContinueButtonClick = () => { - setContinueClicked(true); handleReset(); + setContinueClicked(true); router.push("/"); }; From efce8c0029dbfa82458d33897d88ab814962975b Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 09:37:58 +0800 Subject: [PATCH 02/36] include class as allowed attribute --- app/api/code/submit/submit-helpers.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/api/code/submit/submit-helpers.ts b/app/api/code/submit/submit-helpers.ts index 7a53569..f6baf33 100644 --- a/app/api/code/submit/submit-helpers.ts +++ b/app/api/code/submit/submit-helpers.ts @@ -7,12 +7,13 @@ const DOMPurify = createDOMPurify(window); // Sanitizer function export const validateHTML = (html: string): boolean => { + html = html.replace(/\n/g, " ").replace(/\s+/g, " ").trim(); if (!isHtml(html)) { return false; } const cleanHTML = DOMPurify.sanitize(html, { - ALLOWED_ATTR: ["href", "src", "alt", "title", "style"], + ALLOWED_ATTR: ["href", "src", "alt", "title", "style", "class"], FORBID_TAGS: [ "script", "iframe", From 40e5d40d27e86512516bdbfb386fe44ae3d41d33 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 09:39:07 +0800 Subject: [PATCH 03/36] disallow image and anchor tags --- app/api/code/submit/submit-helpers.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/api/code/submit/submit-helpers.ts b/app/api/code/submit/submit-helpers.ts index f6baf33..a651727 100644 --- a/app/api/code/submit/submit-helpers.ts +++ b/app/api/code/submit/submit-helpers.ts @@ -13,7 +13,7 @@ export const validateHTML = (html: string): boolean => { } const cleanHTML = DOMPurify.sanitize(html, { - ALLOWED_ATTR: ["href", "src", "alt", "title", "style", "class"], + ALLOWED_ATTR: ["style", "class"], FORBID_TAGS: [ "script", "iframe", @@ -22,6 +22,8 @@ export const validateHTML = (html: string): boolean => { "link", "style", "form", + "img", + "a", ], FORBID_ATTR: [ "onerror", From aca8f3005459abe8b6d2307622e1cbc623148615 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 09:57:00 +0800 Subject: [PATCH 04/36] implement localStorage for rating --- .../components/submit-rating-component.tsx | 2 + .../rating-area/rating-component-server.tsx | 57 ++++++++++++++++ .../core/rating-area/rating-component.tsx | 65 ++++--------------- 3 files changed, 73 insertions(+), 51 deletions(-) create mode 100644 components/core/rating-area/rating-component-server.tsx diff --git a/components/core/rating-area/components/submit-rating-component.tsx b/components/core/rating-area/components/submit-rating-component.tsx index 16f5250..d87f4a7 100644 --- a/components/core/rating-area/components/submit-rating-component.tsx +++ b/components/core/rating-area/components/submit-rating-component.tsx @@ -3,6 +3,7 @@ import { usePutRating } from "@/client-side-queries/rq-queries/rating-submit"; import { Button } from "@/components/ui/button"; import { useRatingStore } from "@/data-store/rating-store"; +import { saveToLocalStorage } from "@/lib/localStorage"; import { Check, Loader2 } from "lucide-react"; import { useEffect, useState } from "react"; import toast from "react-hot-toast"; @@ -28,6 +29,7 @@ const RatingSubmitButton = () => { if (mutation.isSuccess) { toast.success("We really appreciated your help!"); setSubmittingRating(false); + saveToLocalStorage("rating", "true"); } }, [mutation.isSuccess]); diff --git a/components/core/rating-area/rating-component-server.tsx b/components/core/rating-area/rating-component-server.tsx new file mode 100644 index 0000000..dd2b369 --- /dev/null +++ b/components/core/rating-area/rating-component-server.tsx @@ -0,0 +1,57 @@ +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import OverallRating from "./components/overall-rating"; +import Grid from "./util/arrange-rating-sections"; +import RatingSubmitButton from "./components/submit-rating-component"; +import UxRating from "./components/ux-rating"; +import FunRating from "./components/fun-rating"; +import { FeedbackModal } from "@/components/landing/feedback/feedback-modal"; + +/* +Ratings are stored as per the following schema: + : + +For example: +- Overall_Rating : 500-100 + - This states that we currently have a total score of 500 over 100 ratings. Which means, on average, we get 5 stars from every rating. +- Ux_Rating : 100-30 + - Implies we have about 3.3333333... stars per rate +- Fun_Rating : 0-2345 + - We got 0 stars on average +*/ +const RatingBodyServer = () => { + return ( +
+ + + Rate Tailspin + + 30 seconds of your time could translates to a lot of + feedback for the team! + + + + + + + + + + +
+ +
+ +
+
+
+ ); +}; + +export default RatingBodyServer; diff --git a/components/core/rating-area/rating-component.tsx b/components/core/rating-area/rating-component.tsx index 9ef1a98..3996805 100644 --- a/components/core/rating-area/rating-component.tsx +++ b/components/core/rating-area/rating-component.tsx @@ -1,57 +1,20 @@ -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import OverallRating from "./components/overall-rating"; -import Grid from "./util/arrange-rating-sections"; -import RatingSubmitButton from "./components/submit-rating-component"; -import UxRating from "./components/ux-rating"; -import FunRating from "./components/fun-rating"; -import { FeedbackModal } from "@/components/landing/feedback/feedback-modal"; +"use client"; -/* -Ratings are stored as per the following schema: - : +import { loadFromLocalStorage } from "@/lib/localStorage"; +import RatingBodyServer from "./rating-component-server"; -For example: -- Overall_Rating : 500-100 - - This states that we currently have a total score of 500 over 100 ratings. Which means, on average, we get 5 stars from every rating. -- Ux_Rating : 100-30 - - Implies we have about 3.3333333... stars per rate -- Fun_Rating : 0-2345 - - We got 0 stars on average -*/ const RatingBody = () => { - return ( -
- - - Rate Tailspin - - 30 seconds of your time could translates to a lot of - feedback for the team! - - - - - - - - - - -
- -
- -
-
-
- ); + const rated = loadFromLocalStorage("rating"); + + if (!rated) { + return ; + } else { + return ( +

+ You've already rated Tailspin. Thanks! +

+ ); + } }; export default RatingBody; From 1c44d7cac49f688b3a6a55a5ef78294ea9f19d89 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 10:06:05 +0800 Subject: [PATCH 05/36] add scroll to faq --- components/landing/faq.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/landing/faq.tsx b/components/landing/faq.tsx index 2a5996c..7a8b6bc 100644 --- a/components/landing/faq.tsx +++ b/components/landing/faq.tsx @@ -15,7 +15,11 @@ const FAQ = () => { id='accordion' > FAQ - + Sounds like an interesting app... What's the catch? From 4bef0e5adec5f32fd7b88001eb74cf0e2e7b89f9 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 10:11:40 +0800 Subject: [PATCH 06/36] ensure localStorage is run in client side --- lib/localStorage.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/localStorage.ts b/lib/localStorage.ts index bf74363..54d6f58 100644 --- a/lib/localStorage.ts +++ b/lib/localStorage.ts @@ -1,6 +1,8 @@ export const saveToLocalStorage = (key: string, value: string) => { try { - localStorage.setItem(key, value); + if (typeof window !== "undefined") { + localStorage.setItem(key, value); + } } catch (error) { console.error("Error saving to localStorage", error); } @@ -8,11 +10,13 @@ export const saveToLocalStorage = (key: string, value: string) => { export const loadFromLocalStorage = (key: string) => { try { - const value = localStorage.getItem(key); - if (value === null) { - return ""; + if (typeof window !== "undefined") { + const value = localStorage.getItem(key); + if (value === null) { + return ""; + } + return value; } - return value; } catch (error) { console.error("Error reading from localStorage", error); return ""; From 2f7b49272d343c8675d46cd1a463cd864b10c988 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 10:12:19 +0800 Subject: [PATCH 07/36] refactor challenge form --- .../test-challenges/forms}/Challenge-FormField.tsx | 0 .../test-challenges/forms}/Email-FormField.tsx | 0 .../test-challenges/forms}/Stepper-Form.tsx | 4 ++-- .../test-challenges/forms}/TOS-FormField.tsx | 0 .../test-challenges/stepper-pages}/StepOne.tsx | 4 ++-- .../test-challenges/stepper-pages}/StepTwo.tsx | 2 +- .../test-challenges/stepper-pages}/StepperCard.tsx | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) rename components/{ui/useCode => landing/test-challenges/forms}/Challenge-FormField.tsx (100%) rename components/{ui/useCode => landing/test-challenges/forms}/Email-FormField.tsx (100%) rename components/{ui/useCode => landing/test-challenges/forms}/Stepper-Form.tsx (82%) rename components/{ui/useCode => landing/test-challenges/forms}/TOS-FormField.tsx (100%) rename components/{ui/useCode => landing/test-challenges/stepper-pages}/StepOne.tsx (95%) rename components/{ui/useCode => landing/test-challenges/stepper-pages}/StepTwo.tsx (97%) rename components/{ui/useCode => landing/test-challenges/stepper-pages}/StepperCard.tsx (94%) diff --git a/components/ui/useCode/Challenge-FormField.tsx b/components/landing/test-challenges/forms/Challenge-FormField.tsx similarity index 100% rename from components/ui/useCode/Challenge-FormField.tsx rename to components/landing/test-challenges/forms/Challenge-FormField.tsx diff --git a/components/ui/useCode/Email-FormField.tsx b/components/landing/test-challenges/forms/Email-FormField.tsx similarity index 100% rename from components/ui/useCode/Email-FormField.tsx rename to components/landing/test-challenges/forms/Email-FormField.tsx diff --git a/components/ui/useCode/Stepper-Form.tsx b/components/landing/test-challenges/forms/Stepper-Form.tsx similarity index 82% rename from components/ui/useCode/Stepper-Form.tsx rename to components/landing/test-challenges/forms/Stepper-Form.tsx index b7f289b..4e549f7 100644 --- a/components/ui/useCode/Stepper-Form.tsx +++ b/components/landing/test-challenges/forms/Stepper-Form.tsx @@ -2,8 +2,8 @@ import { useStepperStore } from "@/data-store/stepper-store"; -import { StepOne } from "./StepOne"; -import { StepTwo } from "./StepTwo"; +import { StepOne } from "../stepper-pages/StepOne"; +import { StepTwo } from "../stepper-pages/StepTwo"; import { useEffect } from "react"; export function StepperForm() { diff --git a/components/ui/useCode/TOS-FormField.tsx b/components/landing/test-challenges/forms/TOS-FormField.tsx similarity index 100% rename from components/ui/useCode/TOS-FormField.tsx rename to components/landing/test-challenges/forms/TOS-FormField.tsx diff --git a/components/ui/useCode/StepOne.tsx b/components/landing/test-challenges/stepper-pages/StepOne.tsx similarity index 95% rename from components/ui/useCode/StepOne.tsx rename to components/landing/test-challenges/stepper-pages/StepOne.tsx index 52c0b72..73b73ba 100644 --- a/components/ui/useCode/StepOne.tsx +++ b/components/landing/test-challenges/stepper-pages/StepOne.tsx @@ -7,8 +7,8 @@ import { useStepperStore, progressIncrements, } from "@/data-store/stepper-store"; -import { EmailFormField } from "./Email-FormField"; -import { TOSFormField } from "./TOS-FormField"; +import { EmailFormField } from "../forms/Email-FormField"; +import { TOSFormField } from "../forms/TOS-FormField"; import { Loader2 } from "lucide-react"; export function StepOne() { diff --git a/components/ui/useCode/StepTwo.tsx b/components/landing/test-challenges/stepper-pages/StepTwo.tsx similarity index 97% rename from components/ui/useCode/StepTwo.tsx rename to components/landing/test-challenges/stepper-pages/StepTwo.tsx index 67cb6e9..a9b21f9 100644 --- a/components/ui/useCode/StepTwo.tsx +++ b/components/landing/test-challenges/stepper-pages/StepTwo.tsx @@ -10,7 +10,7 @@ import { import useSessionStore from "@/data-store/session-store"; import LandingPageCode from "@/components/landing/test-challenges/placeholder-code"; import { useRouter } from "next/navigation"; -import { ChallengeFormField } from "./Challenge-FormField"; +import { ChallengeFormField } from "../forms/Challenge-FormField"; import { Loader2 } from "lucide-react"; import { challengeEnum } from "@/data-store/challenge-store"; import { saveToLocalStorage } from "@/lib/localStorage"; diff --git a/components/ui/useCode/StepperCard.tsx b/components/landing/test-challenges/stepper-pages/StepperCard.tsx similarity index 94% rename from components/ui/useCode/StepperCard.tsx rename to components/landing/test-challenges/stepper-pages/StepperCard.tsx index 21086ea..1db2632 100644 --- a/components/ui/useCode/StepperCard.tsx +++ b/components/landing/test-challenges/stepper-pages/StepperCard.tsx @@ -7,7 +7,7 @@ import { } from "@/components/ui/card"; import { Progress } from "@/components/ui/progress"; -import { StepperForm } from "./Stepper-Form"; +import { StepperForm } from "../forms/Stepper-Form"; export function StepperCard() { return ( From c9a6eac3b8d47d1ce626cd13839bcfdcc75227b6 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 10:12:35 +0800 Subject: [PATCH 08/36] use new challenge form directory --- app/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/page.tsx b/app/page.tsx index 6293a4d..48bd9ab 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -6,7 +6,7 @@ import AboutTailspin from "@/components/landing/about-tailspin"; import FAQ from "@/components/landing/faq"; import Timeline from "@/components/landing/timeline"; import ThanksPage from "@/components/landing/thanks"; -import StepperCard from "@/components/ui/useCode/StepperCard"; +import StepperCard from "@/components/landing/test-challenges/stepper-pages/StepperCard"; import RatingBody from "@/components/core/rating-area/rating-component"; import GrowOnScroll from "@/components/ui/grow-on-scroll"; import Footer from "@/components/landing/footer"; From b1ad7ce8ce5b8e3c024511a6c44f2c93c4be7e9d Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 10:14:49 +0800 Subject: [PATCH 09/36] remove unused components --- .../Challenge-FormField.tsx | 58 ------------ .../enter-code-area-form/Email-FormField.tsx | 33 ------- .../ui/enter-code-area-form/StepOne.tsx | 87 ----------------- .../ui/enter-code-area-form/StepTwo.tsx | 94 ------------------- .../ui/enter-code-area-form/Stepper-Form.tsx | 26 ----- .../ui/enter-code-area-form/StepperCard.tsx | 34 ------- .../ui/enter-code-area-form/TOS-FormField.tsx | 69 -------------- 7 files changed, 401 deletions(-) delete mode 100644 components/ui/enter-code-area-form/Challenge-FormField.tsx delete mode 100644 components/ui/enter-code-area-form/Email-FormField.tsx delete mode 100644 components/ui/enter-code-area-form/StepOne.tsx delete mode 100644 components/ui/enter-code-area-form/StepTwo.tsx delete mode 100644 components/ui/enter-code-area-form/Stepper-Form.tsx delete mode 100644 components/ui/enter-code-area-form/StepperCard.tsx delete mode 100644 components/ui/enter-code-area-form/TOS-FormField.tsx diff --git a/components/ui/enter-code-area-form/Challenge-FormField.tsx b/components/ui/enter-code-area-form/Challenge-FormField.tsx deleted file mode 100644 index 1a89537..0000000 --- a/components/ui/enter-code-area-form/Challenge-FormField.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { challengeMap } from "@/data-store/challenge-store"; - -export function ChallengeFormField(form: any) { - return ( - ( - - Challenge/Level - -
- -
-
- -
- )} - /> - ); -} diff --git a/components/ui/enter-code-area-form/Email-FormField.tsx b/components/ui/enter-code-area-form/Email-FormField.tsx deleted file mode 100644 index 107c32e..0000000 --- a/components/ui/enter-code-area-form/Email-FormField.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import { useStepperStore } from "@/data-store/stepper-store"; - -export function EmailFormField(form: any) { - const { email } = useStepperStore(); - - return ( - ( - - Email - - - - - - )} - /> - ); -} diff --git a/components/ui/enter-code-area-form/StepOne.tsx b/components/ui/enter-code-area-form/StepOne.tsx deleted file mode 100644 index 52c0b72..0000000 --- a/components/ui/enter-code-area-form/StepOne.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { zodResolver } from "@hookform/resolvers/zod"; -import * as z from "zod"; -import { useForm } from "react-hook-form"; -import { Button } from "@/components/ui/button"; -import { Form } from "@/components/ui/form"; -import { - useStepperStore, - progressIncrements, -} from "@/data-store/stepper-store"; -import { EmailFormField } from "./Email-FormField"; -import { TOSFormField } from "./TOS-FormField"; -import { Loader2 } from "lucide-react"; - -export function StepOne() { - const { - progress, - setProgress, - step, - setStep, - email, - setEmail, - check, - setCheck, - } = useStepperStore(); - - const formStepOneSchema = z.object({ - email: z - .string() - .email({ - message: "Invalid email address.", - }) - .default(email), - - accept: z - .literal(true, { - errorMap: () => ({ - message: "You must accept the terms & conditions", - }), - }) - .default(check), - }); - - const form = useForm>({ - resolver: zodResolver(formStepOneSchema), - }); - - async function onContinue(values: z.infer) { - setCheck(values.accept); - - if (values.accept === true) { - const userEmail: string = values.email; - setEmail(userEmail); - setCheck(values.accept); - - if (step === 1) { - setProgress(progress + progressIncrements); - setStep(step + 1); - } - } - } - return ( -
- - - - - {form.formState.isSubmitting ? ( - - ) : ( - - )} - - - ); -} diff --git a/components/ui/enter-code-area-form/StepTwo.tsx b/components/ui/enter-code-area-form/StepTwo.tsx deleted file mode 100644 index db053c4..0000000 --- a/components/ui/enter-code-area-form/StepTwo.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { zodResolver } from "@hookform/resolvers/zod"; -import * as z from "zod"; -import { useForm } from "react-hook-form"; -import { Button } from "@/components/ui/button"; -import { Form } from "@/components/ui/form"; -import { - progressIncrements, - useStepperStore, -} from "@/data-store/stepper-store"; -import useSessionStore from "@/data-store/session-store"; -import LandingPageCode from "@/components/landing/test-challenges/placeholder-code"; -import { useRouter } from "next/navigation"; -import { ChallengeFormField } from "./Challenge-FormField"; -import { Loader2 } from "lucide-react"; -import { challengeEnum } from "@/data-store/challenge-store"; - -const formStepTwoSchema = z.object({ - challenge: z.nativeEnum(challengeEnum, { - errorMap: (issue) => { - switch (issue.code) { - case "invalid_type": - return { message: "Please select a challenge!" }; - default: - return { - message: - "This is not a valid challenge, select a different challenge!", - }; - } - }, - }), -}); - -export function StepTwo() { - const { progress, setProgress, step, setStep, setChallenge } = - useStepperStore(); - const { setCode } = useSessionStore(); - - const router = useRouter(); - - const form = useForm>({ - resolver: zodResolver(formStepTwoSchema), - }); - - async function onSubmit(values: z.infer) { - if (step === 2) { - const selection: string = values.challenge; - setCode(LandingPageCode()); - setChallenge(selection); - setProgress(progress + progressIncrements); - - router.push("/code-area"); - } - } - - async function onBack() { - setProgress(progress - progressIncrements); - setStep(step - 1); - } - - return ( -
-
- - - - - {form.formState.isSubmitting ? ( - - ) : ( - - )} - - -
- ); -} diff --git a/components/ui/enter-code-area-form/Stepper-Form.tsx b/components/ui/enter-code-area-form/Stepper-Form.tsx deleted file mode 100644 index b7f289b..0000000 --- a/components/ui/enter-code-area-form/Stepper-Form.tsx +++ /dev/null @@ -1,26 +0,0 @@ -"use client"; - -import { useStepperStore } from "@/data-store/stepper-store"; - -import { StepOne } from "./StepOne"; -import { StepTwo } from "./StepTwo"; -import { useEffect } from "react"; - -export function StepperForm() { - const { step, setChallenge, setCheck, setEmail, setProgress, setStep } = - useStepperStore(); - - useEffect(() => { - setChallenge(""); - setCheck(false); - setEmail(""); - setProgress(0); - setStep(1); - }, []); - - if (step === 1) { - return ; - } else { - return ; - } -} diff --git a/components/ui/enter-code-area-form/StepperCard.tsx b/components/ui/enter-code-area-form/StepperCard.tsx deleted file mode 100644 index 21086ea..0000000 --- a/components/ui/enter-code-area-form/StepperCard.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; - -import { Progress } from "@/components/ui/progress"; -import { StepperForm } from "./Stepper-Form"; - -export function StepperCard() { - return ( - - - - Try One Of Our Coding Challenges!!! - - - {" "} - Here at TailSpin, we like to have fun!
By filling out - this form, you can use our code editor and attempt to do an - awesome Tailwind UI challenge! -
- -
- - - -
- ); -} - -export default StepperCard; diff --git a/components/ui/enter-code-area-form/TOS-FormField.tsx b/components/ui/enter-code-area-form/TOS-FormField.tsx deleted file mode 100644 index 7ddc36d..0000000 --- a/components/ui/enter-code-area-form/TOS-FormField.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { - FormControl, - FormField, - FormItem, - FormMessage, -} from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import { - progressIncrements, - useStepperStore, -} from "@/data-store/stepper-store"; -import Image from "next/image"; -import Link from "next/link"; - -export function TOSFormField(form: any) { - const { check, setCheck, progress, setProgress } = useStepperStore(); - - return ( - ( - - -
- { - setCheck(!check); - setProgress( - check - ? progress - progressIncrements - : progress + progressIncrements - ); - }} - className='peer mr-[10px] mt-[5px] h-4 w-[20px] cursor-pointer' - /> - -
-
- -
- )} - /> - ); -} From eaa23bbef46af1156c8721b70d422f2f441e0420 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 10:24:26 +0800 Subject: [PATCH 10/36] linting --- app/page.tsx | 2 +- components/core/rating-area/rating-component.tsx | 2 +- components/landing/test-challenges/forms/Stepper-Form.tsx | 2 +- components/landing/test-challenges/stepper-pages/StepOne.tsx | 4 ++-- components/landing/test-challenges/stepper-pages/StepTwo.tsx | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 48bd9ab..d6391f0 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -6,12 +6,12 @@ import AboutTailspin from "@/components/landing/about-tailspin"; import FAQ from "@/components/landing/faq"; import Timeline from "@/components/landing/timeline"; import ThanksPage from "@/components/landing/thanks"; -import StepperCard from "@/components/landing/test-challenges/stepper-pages/StepperCard"; import RatingBody from "@/components/core/rating-area/rating-component"; import GrowOnScroll from "@/components/ui/grow-on-scroll"; import Footer from "@/components/landing/footer"; import ComponentCarousel from "@/components/ui/component-carousel"; import { Separator } from "@/components/ui/separator"; +import StepperCard from "@/components/landing/test-challenges/stepper-pages/StepperCard"; export default function Home() { return ( diff --git a/components/core/rating-area/rating-component.tsx b/components/core/rating-area/rating-component.tsx index 3996805..bab9776 100644 --- a/components/core/rating-area/rating-component.tsx +++ b/components/core/rating-area/rating-component.tsx @@ -11,7 +11,7 @@ const RatingBody = () => { } else { return (

- You've already rated Tailspin. Thanks! + You've already rated Tailspin. Thanks!

); } diff --git a/components/landing/test-challenges/forms/Stepper-Form.tsx b/components/landing/test-challenges/forms/Stepper-Form.tsx index 4e549f7..fbddabf 100644 --- a/components/landing/test-challenges/forms/Stepper-Form.tsx +++ b/components/landing/test-challenges/forms/Stepper-Form.tsx @@ -2,9 +2,9 @@ import { useStepperStore } from "@/data-store/stepper-store"; +import { useEffect } from "react"; import { StepOne } from "../stepper-pages/StepOne"; import { StepTwo } from "../stepper-pages/StepTwo"; -import { useEffect } from "react"; export function StepperForm() { const { step, setChallenge, setCheck, setEmail, setProgress, setStep } = diff --git a/components/landing/test-challenges/stepper-pages/StepOne.tsx b/components/landing/test-challenges/stepper-pages/StepOne.tsx index 73b73ba..f44deea 100644 --- a/components/landing/test-challenges/stepper-pages/StepOne.tsx +++ b/components/landing/test-challenges/stepper-pages/StepOne.tsx @@ -7,9 +7,9 @@ import { useStepperStore, progressIncrements, } from "@/data-store/stepper-store"; -import { EmailFormField } from "../forms/Email-FormField"; -import { TOSFormField } from "../forms/TOS-FormField"; import { Loader2 } from "lucide-react"; +import { TOSFormField } from "../forms/TOS-FormField"; +import { EmailFormField } from "../forms/Email-FormField"; export function StepOne() { const { diff --git a/components/landing/test-challenges/stepper-pages/StepTwo.tsx b/components/landing/test-challenges/stepper-pages/StepTwo.tsx index a9b21f9..ec9a691 100644 --- a/components/landing/test-challenges/stepper-pages/StepTwo.tsx +++ b/components/landing/test-challenges/stepper-pages/StepTwo.tsx @@ -10,10 +10,10 @@ import { import useSessionStore from "@/data-store/session-store"; import LandingPageCode from "@/components/landing/test-challenges/placeholder-code"; import { useRouter } from "next/navigation"; -import { ChallengeFormField } from "../forms/Challenge-FormField"; import { Loader2 } from "lucide-react"; import { challengeEnum } from "@/data-store/challenge-store"; import { saveToLocalStorage } from "@/lib/localStorage"; +import { ChallengeFormField } from "../forms/Challenge-FormField"; const formStepTwoSchema = z.object({ challenge: z.nativeEnum(challengeEnum, { From b7d5fdc55d895d62daf316ae81c94c8da9eb8c43 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 10:30:07 +0800 Subject: [PATCH 11/36] change instructional text --- app/page.tsx | 5 +++-- .../landing/test-challenges/stepper-pages/StepperCard.tsx | 8 +++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index d6391f0..cce0b1f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -41,8 +41,9 @@ export default function Home() {

We hate to be non-inclusive towards phones and - tablets, however we want to provide you with the - best experience possible! + tablets, however coding on small screens is + currently unsupported! Try us on your + laptop/desktop!

diff --git a/components/landing/test-challenges/stepper-pages/StepperCard.tsx b/components/landing/test-challenges/stepper-pages/StepperCard.tsx index 1db2632..66b7f8d 100644 --- a/components/landing/test-challenges/stepper-pages/StepperCard.tsx +++ b/components/landing/test-challenges/stepper-pages/StepperCard.tsx @@ -14,13 +14,11 @@ export function StepperCard() { - Try One Of Our Coding Challenges!!! + Try Out A Coding Challenge! - {" "} - Here at TailSpin, we like to have fun!
By filling out - this form, you can use our code editor and attempt to do an - awesome Tailwind UI challenge! + It's simple. Give us an email, accept the TOS, and + select a challenge.
From 74ac0beb7bf545a53cc6c1d3d0b9380cc03946c8 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 10:32:03 +0800 Subject: [PATCH 12/36] refactor to challenge folder --- app/page.tsx | 2 +- .../forms/Challenge-FormField.tsx | 0 .../{test-challenges => challenge}/forms/Email-FormField.tsx | 0 .../{test-challenges => challenge}/forms/Stepper-Form.tsx | 0 .../{test-challenges => challenge}/forms/TOS-FormField.tsx | 0 .../{test-challenges => challenge}/stepper-pages/StepOne.tsx | 0 .../{test-challenges => challenge}/stepper-pages/StepTwo.tsx | 0 .../stepper-pages/StepperCard.tsx | 0 8 files changed, 1 insertion(+), 1 deletion(-) rename components/landing/{test-challenges => challenge}/forms/Challenge-FormField.tsx (100%) rename components/landing/{test-challenges => challenge}/forms/Email-FormField.tsx (100%) rename components/landing/{test-challenges => challenge}/forms/Stepper-Form.tsx (100%) rename components/landing/{test-challenges => challenge}/forms/TOS-FormField.tsx (100%) rename components/landing/{test-challenges => challenge}/stepper-pages/StepOne.tsx (100%) rename components/landing/{test-challenges => challenge}/stepper-pages/StepTwo.tsx (100%) rename components/landing/{test-challenges => challenge}/stepper-pages/StepperCard.tsx (100%) diff --git a/app/page.tsx b/app/page.tsx index cce0b1f..a26ba92 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -11,7 +11,7 @@ import GrowOnScroll from "@/components/ui/grow-on-scroll"; import Footer from "@/components/landing/footer"; import ComponentCarousel from "@/components/ui/component-carousel"; import { Separator } from "@/components/ui/separator"; -import StepperCard from "@/components/landing/test-challenges/stepper-pages/StepperCard"; +import StepperCard from "@/components/landing/challenge/stepper-pages/StepperCard"; export default function Home() { return ( diff --git a/components/landing/test-challenges/forms/Challenge-FormField.tsx b/components/landing/challenge/forms/Challenge-FormField.tsx similarity index 100% rename from components/landing/test-challenges/forms/Challenge-FormField.tsx rename to components/landing/challenge/forms/Challenge-FormField.tsx diff --git a/components/landing/test-challenges/forms/Email-FormField.tsx b/components/landing/challenge/forms/Email-FormField.tsx similarity index 100% rename from components/landing/test-challenges/forms/Email-FormField.tsx rename to components/landing/challenge/forms/Email-FormField.tsx diff --git a/components/landing/test-challenges/forms/Stepper-Form.tsx b/components/landing/challenge/forms/Stepper-Form.tsx similarity index 100% rename from components/landing/test-challenges/forms/Stepper-Form.tsx rename to components/landing/challenge/forms/Stepper-Form.tsx diff --git a/components/landing/test-challenges/forms/TOS-FormField.tsx b/components/landing/challenge/forms/TOS-FormField.tsx similarity index 100% rename from components/landing/test-challenges/forms/TOS-FormField.tsx rename to components/landing/challenge/forms/TOS-FormField.tsx diff --git a/components/landing/test-challenges/stepper-pages/StepOne.tsx b/components/landing/challenge/stepper-pages/StepOne.tsx similarity index 100% rename from components/landing/test-challenges/stepper-pages/StepOne.tsx rename to components/landing/challenge/stepper-pages/StepOne.tsx diff --git a/components/landing/test-challenges/stepper-pages/StepTwo.tsx b/components/landing/challenge/stepper-pages/StepTwo.tsx similarity index 100% rename from components/landing/test-challenges/stepper-pages/StepTwo.tsx rename to components/landing/challenge/stepper-pages/StepTwo.tsx diff --git a/components/landing/test-challenges/stepper-pages/StepperCard.tsx b/components/landing/challenge/stepper-pages/StepperCard.tsx similarity index 100% rename from components/landing/test-challenges/stepper-pages/StepperCard.tsx rename to components/landing/challenge/stepper-pages/StepperCard.tsx From 3eaee4ff66debe7919a9882fed08899dbae8a9e4 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 10:45:48 +0800 Subject: [PATCH 13/36] add badge --- .../challenge/stepper-pages/StepperCard.tsx | 6 +++- components/ui/badge.tsx | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 components/ui/badge.tsx diff --git a/components/landing/challenge/stepper-pages/StepperCard.tsx b/components/landing/challenge/stepper-pages/StepperCard.tsx index 66b7f8d..17d00eb 100644 --- a/components/landing/challenge/stepper-pages/StepperCard.tsx +++ b/components/landing/challenge/stepper-pages/StepperCard.tsx @@ -8,13 +8,17 @@ import { import { Progress } from "@/components/ui/progress"; import { StepperForm } from "../forms/Stepper-Form"; +import { Badge } from "@/components/ui/badge"; export function StepperCard() { return ( - Try Out A Coding Challenge! +
+

Try Out A Coding Challenge!

+ Alpha +
It's simple. Give us an email, accept the TOS, and diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx new file mode 100644 index 0000000..f000e3e --- /dev/null +++ b/components/ui/badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } From a6d928336ca4b01c2b8f8aa39ada1f0a3b2318d9 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 11:37:26 +0800 Subject: [PATCH 14/36] add return types --- lib/localStorage.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/localStorage.ts b/lib/localStorage.ts index 54d6f58..043054c 100644 --- a/lib/localStorage.ts +++ b/lib/localStorage.ts @@ -1,4 +1,4 @@ -export const saveToLocalStorage = (key: string, value: string) => { +export const saveToLocalStorage = (key: string, value: string): void => { try { if (typeof window !== "undefined") { localStorage.setItem(key, value); @@ -8,22 +8,23 @@ export const saveToLocalStorage = (key: string, value: string) => { } }; -export const loadFromLocalStorage = (key: string) => { +export const loadFromLocalStorage = (key: string): string => { try { if (typeof window !== "undefined") { const value = localStorage.getItem(key); - if (value === null) { + if (value === null || value === undefined) { return ""; } return value; } + return ""; } catch (error) { console.error("Error reading from localStorage", error); return ""; } }; -export const removeItemFromLocalStorage = (key: string) => { +export const removeItemFromLocalStorage = (key: string): void => { if (typeof window !== "undefined") { localStorage.removeItem(key); } From e1ddfe963fa00108cc6162fac59f8df78a99889c Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 11:37:35 +0800 Subject: [PATCH 15/36] add badge --- components/ui/badge.tsx | 54 ++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx index f000e3e..476d402 100644 --- a/components/ui/badge.tsx +++ b/components/ui/badge.tsx @@ -1,36 +1,36 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const badgeVariants = cva( - "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", - { - variants: { - variant: { - default: - "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", - secondary: - "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", - destructive: - "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", - outline: "text-foreground", - }, - }, - defaultVariants: { - variant: "default", - }, - } -) + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +); export interface BadgeProps - extends React.HTMLAttributes, - VariantProps {} + extends React.HTMLAttributes, + VariantProps {} function Badge({ className, variant, ...props }: BadgeProps) { - return ( -
- ) + return ( +
+ ); } -export { Badge, badgeVariants } +export { Badge, badgeVariants }; From e36c1e1c0e5a89f316149b37f5a2f271b9b9bbaf Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 11:37:53 +0800 Subject: [PATCH 16/36] implement cover page for challenge --- app/page.tsx | 6 +- .../landing/challenge/challenge-intro.tsx | 63 +++++++++++++++++++ .../landing/challenge/challenge-main.tsx | 28 +++++++++ 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 components/landing/challenge/challenge-intro.tsx create mode 100644 components/landing/challenge/challenge-main.tsx diff --git a/app/page.tsx b/app/page.tsx index a26ba92..657f276 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -11,7 +11,7 @@ import GrowOnScroll from "@/components/ui/grow-on-scroll"; import Footer from "@/components/landing/footer"; import ComponentCarousel from "@/components/ui/component-carousel"; import { Separator } from "@/components/ui/separator"; -import StepperCard from "@/components/landing/challenge/stepper-pages/StepperCard"; +import ChallengeMain from "@/components/landing/challenge/challenge-main"; export default function Home() { return ( @@ -47,7 +47,7 @@ export default function Home() {

- + @@ -61,7 +61,7 @@ export default function Home() { /> -
+
diff --git a/components/landing/challenge/challenge-intro.tsx b/components/landing/challenge/challenge-intro.tsx new file mode 100644 index 0000000..1e10f1d --- /dev/null +++ b/components/landing/challenge/challenge-intro.tsx @@ -0,0 +1,63 @@ +import { Badge } from "@/components/ui/badge"; +import Link from "next/link"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, + CardFooter, +} from "@/components/ui/card"; +import { Separator } from "@/components/ui/separator"; + +interface ChallengeIntroProps { + button: () => React.ReactNode; +} + +const ChallengeIntro: React.FC = ({ button }) => { + return ( + + + + We're glad you're here! + + + + Alpha + + + + + +

+ It's simple. We'll provide you a coding + environment and a target image. Your job is to recreate that + image using{" "} + HTML{" "} + and{" "} + + TailwindCSS + + . After you submit, we'll send your scores via Email + 📧. +

+
+ +

+ At this stage, we're presenting a bare bones look at + what's to come for Tailspin and looking for{" "} + + feedback + + ! +

+
+ +
+ {button()} +
+
+ ); +}; + +export default ChallengeIntro; diff --git a/components/landing/challenge/challenge-main.tsx b/components/landing/challenge/challenge-main.tsx new file mode 100644 index 0000000..c089107 --- /dev/null +++ b/components/landing/challenge/challenge-main.tsx @@ -0,0 +1,28 @@ +"use client"; + +import { useState } from "react"; +import ChallengeIntro from "./challenge-intro"; +import StepperCard from "./stepper-pages/StepperCard"; +import { Button } from "@/components/ui/button"; +import { Code } from "lucide-react"; + +const ChallengeMain = () => { + const [goToStepper, setGoToStepper] = useState(false); + + const readyToCodeButton = () => { + return ( + + ); + }; + return ( + <> + {!goToStepper && } + {goToStepper && } + + ); +}; + +export default ChallengeMain; From 3b2870f187f3fbd3347125a89af07018d726de73 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 12:32:00 +0800 Subject: [PATCH 17/36] add info on user accounts --- components/landing/challenge/challenge-intro.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/landing/challenge/challenge-intro.tsx b/components/landing/challenge/challenge-intro.tsx index 1e10f1d..08ecb32 100644 --- a/components/landing/challenge/challenge-intro.tsx +++ b/components/landing/challenge/challenge-intro.tsx @@ -39,7 +39,13 @@ const ChallengeIntro: React.FC = ({ button }) => { TailwindCSS . After you submit, we'll send your scores via Email - 📧. + 📧.
+
Currently, we only provide a playground which + represents the core services of Tailspin. When you submit + code from our platform, we score your code and email the + result of similarity to you. We don't have a way for + you to track all your previous submissions or rank yourself + against other developers trying out Tailspin.

From 03a9cd058956ded35b66ba2ccb23be077f787cee Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 12:32:08 +0800 Subject: [PATCH 18/36] make accordion text smaller --- components/ui/accordion.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ui/accordion.tsx b/components/ui/accordion.tsx index be5e311..f42a988 100644 --- a/components/ui/accordion.tsx +++ b/components/ui/accordion.tsx @@ -28,7 +28,7 @@ const AccordionTrigger = React.forwardRef< svg]:rotate-180", + "text-md flex flex-1 items-center justify-between py-4 font-medium text-muted-foreground transition-all hover:underline [&[data-state=open]>svg]:rotate-180", className )} {...props} From 9ac9940db29f03490174b7feb93c51dbc05525da Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 12:32:18 +0800 Subject: [PATCH 19/36] add 2 faqs --- components/landing/faq.tsx | 41 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/components/landing/faq.tsx b/components/landing/faq.tsx index 7a8b6bc..5d86841 100644 --- a/components/landing/faq.tsx +++ b/components/landing/faq.tsx @@ -194,6 +194,47 @@ const FAQ = () => {

+ + + When are we implementing accounts? + + + In this Alpha stage, our plan is to gather feedback from + users before we roll out stateful user accounts. We're + doing this to test the waters and viability of the + concept of Tailspin because developing good quality + software takes time and effort. That said, if you + haven't given us feedback, you may do so{" "} + + here + + ! + + + + + Why do I have to provide my email in the playground? + + + To make it harder for bad people to spam us and to + provide you with a better experience. Tailspin's + tech stack includes a managed database, GPT3.5, and + serverless compute. We don't want our resources being + abused so we insist on an email so that it becomes an + extra layer of effort for bad actors to misuse Tailspin. + As mentioned, Tailspin uses GPT3.5 and serverless + compute. These technologies can take a long time, + depending on the total load on these services from other + applications. As such, in order to facilitate an + asynchronous processing experience, we are essentially + running these longer tasks in the background; so we have + to send you the results of processing in an asynchronous + fashion too - email! + +
); From 3cf56b6646619f8df4435bf4c5396017022f916b Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 12:44:35 +0800 Subject: [PATCH 20/36] implement contact form --- components/landing/footer.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/components/landing/footer.tsx b/components/landing/footer.tsx index d7599af..305754f 100644 --- a/components/landing/footer.tsx +++ b/components/landing/footer.tsx @@ -1,5 +1,7 @@ import Link from "next/link"; import { TailspinLogo } from "../ui/spinning-logo"; +import { Button } from "../ui/button"; +import { FeedbackModal } from "./feedback/feedback-modal"; const Footer = () => { return ( @@ -17,17 +19,14 @@ const Footer = () => { From b237ab49c2e30306c390c982f15df061c8f806aa Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 12:45:40 +0800 Subject: [PATCH 21/36] change wording --- app/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/page.tsx b/app/page.tsx index 657f276..a3ac867 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -57,7 +57,7 @@ export default function Home() {
, ]} - title='Some useless information' + title='Incase you were wondering...' />
From 1287b5c555ba7b3483088e5332f377cd6c8a7e09 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 12:50:24 +0800 Subject: [PATCH 22/36] place green border around scroll area --- components/landing/thanks.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/landing/thanks.tsx b/components/landing/thanks.tsx index aae36da..70fc5b7 100644 --- a/components/landing/thanks.tsx +++ b/components/landing/thanks.tsx @@ -139,8 +139,8 @@ const ThanksPage = async () => { Tech that made this possible! ({techs.size}) -
- + <> + {Array.from(techs.entries()).map(([url, name]) => (
@@ -156,7 +156,7 @@ const ThanksPage = async () => { ))} -
+
From 0677f0a5e323afb8d1463813ef11df4108ffe54c Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 13:13:18 +0800 Subject: [PATCH 23/36] add more detail --- .../landing/challenge/challenge-intro.tsx | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/components/landing/challenge/challenge-intro.tsx b/components/landing/challenge/challenge-intro.tsx index 08ecb32..a1bbe2b 100644 --- a/components/landing/challenge/challenge-intro.tsx +++ b/components/landing/challenge/challenge-intro.tsx @@ -19,20 +19,23 @@ const ChallengeIntro: React.FC = ({ button }) => { - We're glad you're here! + 🥳 We're glad you're here! - + Alpha + + MVP +

It's simple. We'll provide you a coding - environment and a target image. Your job is to recreate that - image using{" "} + environment and a target image 🖼️. Your job is to recreate + that image using{" "} HTML{" "} and{" "} @@ -43,9 +46,12 @@ const ChallengeIntro: React.FC = ({ button }) => {
Currently, we only provide a playground which represents the core services of Tailspin. When you submit code from our platform, we score your code and email the - result of similarity to you. We don't have a way for - you to track all your previous submissions or rank yourself - against other developers trying out Tailspin. + result of similarity to you.{" "} + + In this MVP, we don't have a way for you to track + all your previous submissions or rank yourself against + other developers trying out Tailspin. +

From a8bd3bbb0c566d5527d5cccf77a411d3434a7080 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 13:13:27 +0800 Subject: [PATCH 24/36] add tooltip --- components/ui/tooltip.tsx | 30 ++++++++++++++++++++++++++++++ package.json | 1 + yarn.lock | 19 +++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 components/ui/tooltip.tsx diff --git a/components/ui/tooltip.tsx b/components/ui/tooltip.tsx new file mode 100644 index 0000000..0990b90 --- /dev/null +++ b/components/ui/tooltip.tsx @@ -0,0 +1,30 @@ +"use client"; + +import * as React from "react"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; + +import { cn } from "@/lib/utils"; + +const TooltipProvider = TooltipPrimitive.Provider; + +const Tooltip = TooltipPrimitive.Root; + +const TooltipTrigger = TooltipPrimitive.Trigger; + +const TooltipContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + +)); +TooltipContent.displayName = TooltipPrimitive.Content.displayName; + +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; diff --git a/package.json b/package.json index 44d3919..0180e25 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-tabs": "^1.0.4", + "@radix-ui/react-tooltip": "^1.0.7", "@react-email/components": "^0.0.7", "@react-email/render": "^0.0.7", "@tanstack/react-query": "^4.35.3", diff --git a/yarn.lock b/yarn.lock index 3ebafbd..f8cff22 100644 --- a/yarn.lock +++ b/yarn.lock @@ -674,6 +674,25 @@ "@radix-ui/react-roving-focus" "1.0.4" "@radix-ui/react-use-controllable-state" "1.0.1" +"@radix-ui/react-tooltip@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz#8f55070f852e7e7450cc1d9210b793d2e5a7686e" + integrity sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-dismissable-layer" "1.0.5" + "@radix-ui/react-id" "1.0.1" + "@radix-ui/react-popper" "1.1.3" + "@radix-ui/react-portal" "1.0.4" + "@radix-ui/react-presence" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-slot" "1.0.2" + "@radix-ui/react-use-controllable-state" "1.0.1" + "@radix-ui/react-visually-hidden" "1.0.3" + "@radix-ui/react-use-callback-ref@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a" From 231fd520ea888d5513b9e8f4b4348cca0a3348a2 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 13:13:33 +0800 Subject: [PATCH 25/36] implement tooltip --- components/landing/hero.tsx | 66 +++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/components/landing/hero.tsx b/components/landing/hero.tsx index c2fb108..be98d94 100644 --- a/components/landing/hero.tsx +++ b/components/landing/hero.tsx @@ -3,6 +3,12 @@ import { HeroSubText } from "@/components/ui/typewriter-effect"; import HeroPageToast from "./hero-page-toast"; import { Button } from "../ui/button"; import Link from "next/link"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; export const Hero = () => { return ( @@ -20,12 +26,12 @@ export const Hero = () => {
- Forever free. Not affiliated with{" "} + Forever free 💰. Not affiliated with{" "} { The Tailwind Org
- -
- -
-
- -
-
- -
- + + + + +
+ +
+
+ +
+
+ +
+ +
+ +

Jump to coding section

+
+
+
+ ); From 7859a86c9447f45c1c2d7b78467a53708e4628ff Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 13:31:09 +0800 Subject: [PATCH 26/36] add stars to site stats section --- components/landing/stats.tsx | 2 +- .../ui/statistics/code-submit-count-box.tsx | 2 +- components/ui/statistics/site-rating.tsx | 32 +++++++++++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/components/landing/stats.tsx b/components/landing/stats.tsx index 0514e71..eb6cbc6 100644 --- a/components/landing/stats.tsx +++ b/components/landing/stats.tsx @@ -4,7 +4,7 @@ import { SiteVisitCountBox } from "../ui/statistics/site-visit-count-box"; const SiteCounter = async () => { return ( -
+

Some Statistics

diff --git a/components/ui/statistics/code-submit-count-box.tsx b/components/ui/statistics/code-submit-count-box.tsx index 889bcf8..3fb7d93 100644 --- a/components/ui/statistics/code-submit-count-box.tsx +++ b/components/ui/statistics/code-submit-count-box.tsx @@ -9,7 +9,7 @@ export const CodeSubmitCountBox = (props: any) => { return ( } - content={`How many people have submitted their code : ${value}`} + content={`Submissions to date: ${value}`} /> ); }; diff --git a/components/ui/statistics/site-rating.tsx b/components/ui/statistics/site-rating.tsx index 3de9aa4..aaaa5be 100644 --- a/components/ui/statistics/site-rating.tsx +++ b/components/ui/statistics/site-rating.tsx @@ -18,8 +18,21 @@ function SkeletonDemo() { ); } -export const SiteRatingStatBox = (props: any) => { +const RatingStars = ({ score }: { score: string }) => { + const rating = parseInt(score, 10); + const stars = Array(rating).fill(null); + return ( +
+ {stars.map((_, index) => ( + // Replace with your Star icon component + ))} +
+ ); +}; + +export const SiteRatingStatBox = () => { const value = useGetRating(); + return ( <> {value ? ( @@ -27,8 +40,21 @@ export const SiteRatingStatBox = (props: any) => { (val: { field_name: string; field_value: string }) => ( } - content={`${val.field_name}:${( + icon={ + + } + content={`${val.field_name + .split("_") + .join(" ")}: ${( Number(val.field_value.split("-")[0]) / Number(val.field_value.split("-")[1]) ).toFixed(2)} with ${ From 9a0fd97e67f627afbac408f472d478a427d49917 Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 13:49:51 +0800 Subject: [PATCH 27/36] shrink card on small devices --- components/landing/challenge/challenge-intro.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/landing/challenge/challenge-intro.tsx b/components/landing/challenge/challenge-intro.tsx index a1bbe2b..ef4e930 100644 --- a/components/landing/challenge/challenge-intro.tsx +++ b/components/landing/challenge/challenge-intro.tsx @@ -16,7 +16,7 @@ interface ChallengeIntroProps { const ChallengeIntro: React.FC = ({ button }) => { return ( - + 🥳 We're glad you're here! From bd8551f3210f963bb1c66096f48f972fb15b8ceb Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 13:56:17 +0800 Subject: [PATCH 28/36] make carousel responsive --- components/ui/component-carousel.tsx | 59 +++++++++++++++++++--------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/components/ui/component-carousel.tsx b/components/ui/component-carousel.tsx index 42d3e1e..354ee32 100644 --- a/components/ui/component-carousel.tsx +++ b/components/ui/component-carousel.tsx @@ -15,33 +15,54 @@ const ComponentCarousel: React.FC = ({ }) => { const [page, setPage] = useState(0); + const leftArrow = () => { + return ( + + ); + }; + + const rightArrow = () => { + return ( + + ); + }; + + const pageNumber = () => { + return ( +

+ {page + 1}/{nodes.length} +

+ ); + }; + return (

{title}

- +
{leftArrow()}
{nodes[page]} -

- {page + 1}/{nodes.length} -

+
{pageNumber()}
- +
{rightArrow()}
+
+
+ {leftArrow()} + {rightArrow()}
+
{pageNumber()}
); }; From 601008dc680c236e531dc0d97312994731ee5c1d Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 13:58:49 +0800 Subject: [PATCH 29/36] stats in a rows of 2 only in lg screen --- components/landing/stats.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/landing/stats.tsx b/components/landing/stats.tsx index eb6cbc6..26703a0 100644 --- a/components/landing/stats.tsx +++ b/components/landing/stats.tsx @@ -8,7 +8,7 @@ const SiteCounter = async () => {

Some Statistics

-
+
From 617ffccd80462516f86c29f128d46867982027ee Mon Sep 17 00:00:00 2001 From: Zac Date: Wed, 6 Dec 2023 14:02:50 +0800 Subject: [PATCH 30/36] add logo to about title --- components/landing/about-tailspin.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/landing/about-tailspin.tsx b/components/landing/about-tailspin.tsx index 699e732..20e6cea 100644 --- a/components/landing/about-tailspin.tsx +++ b/components/landing/about-tailspin.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import { Button } from "@/components/ui/button"; import { BarChartBig } from "lucide-react"; import FlipOnScroll from "../ui/flip-on-scroll"; +import { TailspinLogo } from "../ui/spinning-logo"; type AboutTailSpinBoxesProps = { title: string; @@ -37,8 +38,8 @@ const AboutTailspin = () => { className='relative flex h-full flex-col items-center justify-center rounded-lg bg-black p-6' id='about-section' > -

- Tailspin +

+ Tailspin

Date: Wed, 6 Dec 2023 14:04:05 +0800 Subject: [PATCH 31/36] linting --- components/landing/faq.tsx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/components/landing/faq.tsx b/components/landing/faq.tsx index 5d86841..e43642b 100644 --- a/components/landing/faq.tsx +++ b/components/landing/faq.tsx @@ -200,11 +200,11 @@ const FAQ = () => { In this Alpha stage, our plan is to gather feedback from - users before we roll out stateful user accounts. We're - doing this to test the waters and viability of the - concept of Tailspin because developing good quality - software takes time and effort. That said, if you - haven't given us feedback, you may do so{" "} + users before we roll out stateful user accounts. + We're doing this to test the waters and viability + of the concept of Tailspin because developing good + quality software takes time and effort. That said, if + you haven't given us feedback, you may do so{" "} { To make it harder for bad people to spam us and to provide you with a better experience. Tailspin's tech stack includes a managed database, GPT3.5, and - serverless compute. We don't want our resources being - abused so we insist on an email so that it becomes an - extra layer of effort for bad actors to misuse Tailspin. - As mentioned, Tailspin uses GPT3.5 and serverless - compute. These technologies can take a long time, - depending on the total load on these services from other - applications. As such, in order to facilitate an + serverless compute. We don't want our resources + being abused so we insist on an email so that it becomes + an extra layer of effort for bad actors to misuse + Tailspin. As mentioned, Tailspin uses GPT3.5 and + serverless compute. These technologies can take a long + time, depending on the total load on these services from + other applications. As such, in order to facilitate an asynchronous processing experience, we are essentially running these longer tasks in the background; so we have to send you the results of processing in an asynchronous From 7da531d3f378396bc6a9e35443328888cfc12a15 Mon Sep 17 00:00:00 2001 From: Zac Date: Fri, 8 Dec 2023 07:42:42 +0800 Subject: [PATCH 32/36] put logo above text --- components/landing/about-tailspin.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/landing/about-tailspin.tsx b/components/landing/about-tailspin.tsx index 20e6cea..b48a6de 100644 --- a/components/landing/about-tailspin.tsx +++ b/components/landing/about-tailspin.tsx @@ -38,8 +38,9 @@ const AboutTailspin = () => { className='relative flex h-full flex-col items-center justify-center rounded-lg bg-black p-6' id='about-section' > +

- Tailspin + Tailspin

Date: Fri, 8 Dec 2023 07:48:35 +0800 Subject: [PATCH 33/36] modify timeline details --- components/landing/timeline.tsx | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/components/landing/timeline.tsx b/components/landing/timeline.tsx index 4bf47cf..cfaa18b 100644 --- a/components/landing/timeline.tsx +++ b/components/landing/timeline.tsx @@ -22,35 +22,22 @@ const Timeline = () => {

    -
  1. - -

    - V0 - - Current - -

    - -

    - Building out this landing page. Polishing the - interactive playground. Adding nice UI/UX. -

    -
  2. V1.0 + + Current +

    - Innovate and make incremental improvements to the - core business of Tailspin - the coding page. - Iteratively add, optimize, and beautify features to - the coding page. + Bare bones MVP. Users can try the coding challenges + and explore Tailspin. We'll work on the + feedback we receive from our users and improve our + MVP.

  3. From fe22044e92d97280911635e3857638cc0b5c2400 Mon Sep 17 00:00:00 2001 From: Zac Date: Fri, 8 Dec 2023 07:57:55 +0800 Subject: [PATCH 34/36] add motivation faq --- components/landing/faq.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/components/landing/faq.tsx b/components/landing/faq.tsx index e43642b..33af386 100644 --- a/components/landing/faq.tsx +++ b/components/landing/faq.tsx @@ -235,6 +235,19 @@ const FAQ = () => { fashion too - email! + + + What was the motivation behind Tailspin? + + + Wise man once said: "There's no better way to learn + than to do." At the time of writing, there isn't a + mainstream online TailwindCSS centered platform, where + we can write Tailwind powered UI building code and + challenge ourselves and one another in the process. + Tailspin hopes to close that gap in the community. + +
); From 6e405ee4c35741a5169f62ad500b5da0d40b0d04 Mon Sep 17 00:00:00 2001 From: Zac Date: Fri, 8 Dec 2023 07:58:04 +0800 Subject: [PATCH 35/36] reword footer text --- components/landing/footer.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/components/landing/footer.tsx b/components/landing/footer.tsx index 305754f..ea5c56d 100644 --- a/components/landing/footer.tsx +++ b/components/landing/footer.tsx @@ -32,11 +32,13 @@ const Footer = () => {

- No © 2023{" "} - - Tailspin + Built because we love it. Source code available in{" "} + + Github - . No Rights Reserved.
From db2789a0d927f969461b76b12cd51ecd35acd12c Mon Sep 17 00:00:00 2001 From: Erick Date: Sat, 9 Dec 2023 16:48:37 -0500 Subject: [PATCH 36/36] replace quote literals with encoded quotes --- components/landing/faq.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/components/landing/faq.tsx b/components/landing/faq.tsx index 33af386..9b91697 100644 --- a/components/landing/faq.tsx +++ b/components/landing/faq.tsx @@ -240,12 +240,13 @@ const FAQ = () => { What was the motivation behind Tailspin? - Wise man once said: "There's no better way to learn - than to do." At the time of writing, there isn't a - mainstream online TailwindCSS centered platform, where - we can write Tailwind powered UI building code and - challenge ourselves and one another in the process. - Tailspin hopes to close that gap in the community. + Wise man once said: "There's no better way to + learn than to do." At the time of writing, there + isn't a mainstream online TailwindCSS centered + platform, where we can write Tailwind powered UI + building code and challenge ourselves and one another in + the process. Tailspin hopes to close that gap in the + community.