Skip to content

Commit

Permalink
feat: support page
Browse files Browse the repository at this point in the history
* support page with multiple forms support
* initially, a DMCA form has been added

Signed-off-by: GitHub <noreply@github.com>
  • Loading branch information
virtual-designer authored Aug 28, 2024
1 parent 8f0f540 commit ea90d45
Show file tree
Hide file tree
Showing 11 changed files with 532 additions and 1 deletion.
11 changes: 11 additions & 0 deletions src/app/(main)/support/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Footer from "@/components/Layout/Footer";
import { PropsWithChildren } from "react";

export default function Layout({ children }: PropsWithChildren) {
return (
<>
<div className="min-h-screen block">{children}</div>
<Footer />
</>
);
}
22 changes: 22 additions & 0 deletions src/app/(main)/support/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import SupportForm from "@/features/SupportForm/SupportForm";
import { ServerComponentProps } from "@/types/ServerComponentProps";
import { Spacer } from "@nextui-org/react";

export default function Page({ searchParams }: ServerComponentProps) {
const form = searchParams?.form;

return (
<main className="flex items-center justify-center py-5 lg:py-10 px-3">
<div className="md:w-96 lg:w-1/2 xl:w-1/3">
<h1 className="text-4xl font-bold">Support</h1>
<p>
You can contact our support team by filling out the form
below.
</p>

<Spacer y={5} />
<SupportForm form={form} />
</div>
</main>
);
}
45 changes: 45 additions & 0 deletions src/features/SupportForm/CommonFields.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Input, Spacer } from "@nextui-org/react";
import {
Control,
FieldValues,
FormState,
Path,
UseFormRegister,
} from "react-hook-form";

type CommonFieldsProps<T extends FieldValues> = {
control: Control<T>;
register: UseFormRegister<T>;
formState: FormState<T>;
};

const CommonFields = <T extends FieldValues>({
control,
register,
formState,
}: CommonFieldsProps<T>) => {
return (
<>
<Input
label="Your name"
isInvalid={Boolean(formState.errors.name)}
errorMessage={formState.errors.name?.message?.toString()}
{...register("name" as Path<T>)}
/>
<Spacer y={2} />
<Input
label="Email address"
isInvalid={Boolean(formState.errors.email)}
errorMessage={formState.errors.email?.message?.toString()}
{...register("email" as Path<T>)}
/>
<p className="text-[#999] text-xs ml-1 mt-1">
We&rsquo;ll use this email address to contact you back if
necessary.
</p>
<div className="w-full h-[1px] bg-neutral-300 dark:bg-neutral-700 block my-5"></div>
</>
);
};

export default CommonFields;
214 changes: 214 additions & 0 deletions src/features/SupportForm/DMCAForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { Input, Radio, RadioGroup, Spacer, Textarea } from "@nextui-org/react";
import clsx from "clsx";
import { useRef, useState, type FC } from "react";
import { Controller, Form, useForm } from "react-hook-form";
import { z } from "zod";
import CommonFields from "./CommonFields";
import DMCASwornStatements from "./DMCASwornStatements";
import SubmitButton from "./SubmitButton";
import { DMCAFormSchema } from "./SupportFormSchemas";

type DMCAFormProps = {};

const DMCAForm: FC<DMCAFormProps> = (props) => {
const [swornStatementsError, setSwornStatementsError] = useState<
string | null
>(null);
const { control, register, formState } = useForm<
z.infer<typeof DMCAFormSchema>
>({
resolver: zodResolver(DMCAFormSchema),
});
const swornStatementsRef = useRef<[boolean, boolean, boolean]>([
false,
false,
false,
]);

const handleSubmit = ({
data,
}: {
data: z.infer<typeof DMCAFormSchema>;
}) => {
if (!swornStatementsRef.current.every(Boolean)) {
setSwornStatementsError(
"Please agree to all the sworn statements.",
);
return;
}

setSwornStatementsError(null);
console.log(data);
};

return (
<Form control={control} onSubmit={handleSubmit}>
<CommonFields
control={control}
register={register}
formState={formState}
/>

<h2 className="text-2xl">Details of the Infringement</h2>
<p className="text-[#999] text-sm mb-5">
Please provide the following details about the infringement.
</p>

<Controller
name="infringingURLs"
control={control}
render={({ field, fieldState }) => (
<Textarea
label="Infringing URLs"
isInvalid={Boolean(fieldState.error)}
errorMessage={fieldState.error?.message?.toString()}
onBlur={field.onBlur}
onValueChange={(value) => {
field.onChange(
(value || "")
.split("\n")
.map((str) => str.trim())
.filter(Boolean),
);
}}
/>
)}
/>
<p className="text-[#999] text-xs ml-1 mt-1">
Please enter the URLs that are infringing your content. You can
enter multiple URLs by separating them with a new line.
<br />
If the infringing content is a Discord Message, please provide
the message link.
</p>

<Spacer y={3} />
<Controller
name="originalContentURLs"
control={control}
render={({ field, fieldState }) => (
<Textarea
label="URLs to the Original Content"
isInvalid={Boolean(fieldState.error)}
errorMessage={fieldState.error?.message?.toString()}
onBlur={field.onBlur}
onValueChange={(value) => {
field.onChange(
(value || "")
.split("\n")
.map((str) => str.trim())
.filter(Boolean),
);
}}
/>
)}
/>
<p className="text-[#999] text-xs ml-1 mt-1">
Please enter the URLs to the original content. You can enter
multiple URLs by separating them with a new line.
</p>

<Spacer y={3} />
<Textarea
label="Description of the Infringement"
isInvalid={Boolean(formState.errors.description)}
errorMessage={formState.errors.description?.message?.toString()}
{...register("description")}
/>

<h2 className="text-2xl mt-7">Your Information</h2>
<p className="text-[#999] text-sm mb-5">
Please provide your information below.
</p>

<Spacer y={3} />
<Input
label="Your Full Legal Name"
isInvalid={Boolean(formState.errors.legalName)}
errorMessage={formState.errors.legalName?.message?.toString()}
{...register("legalName")}
/>

<Spacer y={3} />
<Input
label="Your Company Name (if applicable)"
isInvalid={Boolean(formState.errors.companyName)}
errorMessage={formState.errors.companyName?.message?.toString()}
{...register("companyName")}
/>

<Spacer y={3} />
<Input
label="Your Position in the Company (if applicable)"
isInvalid={Boolean(formState.errors.position)}
errorMessage={formState.errors.position?.message?.toString()}
{...register("position")}
/>

<Spacer y={3} />
<Controller
name="actingOnBehalfOf"
control={control}
render={({ field, fieldState }) => (
<>
<RadioGroup
label="I am acting on behalf of:"
isInvalid={Boolean(fieldState.error)}
errorMessage={fieldState.error?.message?.toString()}
onValueChange={field.onChange}
>
<Radio value="myself">Myself</Radio>
<Radio value="company">My Company</Radio>
<Radio value="client">My Client</Radio>
<Radio value="other">
Other (please specify below)
</Radio>
</RadioGroup>

<Input
label="On behalf of"
isInvalid={Boolean(
formState.errors.actingOnBehalfOfOther,
)}
errorMessage={formState.errors.actingOnBehalfOfOther?.message?.toString()}
className={clsx(
"mt-3",
field.value === "other" ? "" : "hidden",
)}
{...register("actingOnBehalfOfOther")}
/>
</>
)}
/>

<Spacer y={5} />
<DMCASwornStatements
onValueChange={(index, values) => {
swornStatementsRef.current[index] = values;
}}
/>
{swornStatementsError && (
<p className="text-red-500 text-sm mt-2">
{swornStatementsError}
</p>
)}

<Spacer y={5} />
<h2 className="text-2xl">Signature</h2>
<p className="text-[#999] text-sm mb-5">Please sign this form.</p>

<Spacer y={3} />
<Input
label="Your Signature"
isInvalid={Boolean(formState.errors.signature)}
errorMessage={formState.errors.signature?.message?.toString()}
{...register("signature")}
/>

<SubmitButton />
</Form>
);
};

export default DMCAForm;
40 changes: 40 additions & 0 deletions src/features/SupportForm/DMCASwornStatements.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Checkbox } from "@nextui-org/react";
import { type FC } from "react";

type DMCASwornStatementsProps = {
onValueChange: (index: number, value: boolean) => void;
};

const DMCASwornStatements: FC<DMCASwornStatementsProps> = ({
onValueChange,
}) => {
return (
<div>
<Checkbox onValueChange={(value) => onValueChange(0, value)}>
<span className="text-sm">
I have a good faith belief that the use of the material in
the manner complained of is not authorized by the copyright
owner, its agent, or the law.
</span>
</Checkbox>

<Checkbox onValueChange={(value) => onValueChange(1, value)}>
<span className="text-sm">
I swear, under penalty of perjury, that the information in
the notification is accurate, and that I am the owner of an
exclusive right that is allegedly infringed.
</span>
</Checkbox>

<Checkbox onValueChange={(value) => onValueChange(2, value)}>
<span className="text-sm">
I acknowledge that I may be liable for damages if I
knowingly materially misrepresent that material or activity
is infringing.
</span>
</Checkbox>
</div>
);
};

export default DMCASwornStatements;
30 changes: 30 additions & 0 deletions src/features/SupportForm/GeneralHelpForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { type FC } from "react";
import { Form, useForm } from "react-hook-form";
import { z } from "zod";
import CommonFields from "./CommonFields";
import SubmitButton from "./SubmitButton";
import { GeneralHelpFormSchema } from "./SupportFormSchemas";

type GeneralHelpFormProps = {};

const GeneralHelpForm: FC<GeneralHelpFormProps> = (props) => {
const { control, register, formState } = useForm<
z.infer<typeof GeneralHelpFormSchema>
>({
resolver: zodResolver(GeneralHelpFormSchema),
});

return (
<Form control={control}>
<CommonFields
control={control}
register={register}
formState={formState}
/>
<SubmitButton />
</Form>
);
};

export default GeneralHelpForm;
14 changes: 14 additions & 0 deletions src/features/SupportForm/SubmitButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Button, Spacer } from "@nextui-org/react";

const SubmitButton = () => {
return (
<>
<Spacer y={5} />
<Button type="submit" variant="flat" fullWidth>
Submit
</Button>
</>
);
};

export default SubmitButton;
Loading

0 comments on commit ea90d45

Please sign in to comment.