Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CreateTeamForm PR fixes
Browse files Browse the repository at this point in the history
Add usage of Zod
Minor type fixes

refs TS-1942, TS-1829
Oksamies committed Nov 15, 2023
1 parent e8b0132 commit 02b4d89
Showing 6 changed files with 93 additions and 78 deletions.
4 changes: 3 additions & 1 deletion packages/cyberstorm/package.json
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@
"@fortawesome/pro-solid-svg-icons": "6.2.0",
"@fortawesome/pro-thin-svg-icons": "6.2.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@hookform/resolvers": "^3.3.2",
"@radix-ui/react-checkbox": "^1.0.1",
"@radix-ui/react-dialog": "^1.0.2",
"@radix-ui/react-dropdown-menu": "^2.0.1",
@@ -42,7 +43,8 @@
"react-markdown": "^8.0.7",
"remark-gfm": "^3.0.1",
"styled-components": "^6.0.0-rc.5",
"use-debounce": "^9.0.4"
"use-debounce": "^9.0.4",
"zod": "^3.22.2"
},
"devDependencies": {
"typescript-plugin-css-modules": "^3.4.0"
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
.createTeamDialog {
.root {
display: flex;
flex-direction: column;
gap: var(--space--24);
gap: 2rem;
}

.createTeamForm {
.dialog {
display: flex;
flex-direction: column;
gap: 2rem;
gap: var(--space--24);
}

.createTeamDialogText {
.dialogText {
color: var(--color-text--secondary);
font-weight: var(--font-weight-medium);
font-size: var(--font-size--l);
Original file line number Diff line number Diff line change
@@ -1,73 +1,44 @@
"use client";
import styles from "./CreateTeamForm.module.css";
import { faArrowsRotate } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useController, useForm, Form } from "react-hook-form";
import * as Button from "../../Button";
import { TextInput } from "../../TextInput/TextInput";
import { useForm, FieldErrors, useController } from "react-hook-form";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faArrowsRotate } from "@fortawesome/free-solid-svg-icons";
import { useToast } from "../../Toast/Provider";
import styles from "./CreateTeamForm.module.css";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { isRecord } from "../../../utils/type_guards";

import { useContext } from "react";
import { ToastContext } from "../../Toast/ToastContext";
import {
faCircleCheck,
faCircleExclamation,
faOctagonExclamation,
} from "@fortawesome/pro-solid-svg-icons";
interface errorResponse {
error: { message: string };
}

function isErrorResponse(response: unknown): response is errorResponse {
return (
isRecord(response) &&
isRecord(response.error) &&
typeof response.error.message === "string"
);
}

/**
* Form for creating a team
*/
export function CreateTeamForm() {
const useToast = () => useContext(ToastContext);
const toast = useToast();

async function onValid(data: { teamName: string }) {
// TODO: Add sending to TS API endpoint
toast?.addToast(
"info",
<FontAwesomeIcon icon={faCircleExclamation} />,
"Creating team"
);
await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
// TODO: Add a toast on response return based on the response
toast?.addToast(
"success",
<FontAwesomeIcon icon={faCircleCheck} />,
"Team created"
);
console.log(JSON.stringify(data));
interface formFields {
teamName: string;
}

async function onInvalid(
errors: FieldErrors<{
teamName: string;
}>
) {
if (errors.teamName) {
toast?.addToast(
"danger",
<FontAwesomeIcon icon={faOctagonExclamation} />,
errors.teamName.message,
30000
);
} else {
toast?.addToast(
"danger",
<FontAwesomeIcon icon={faOctagonExclamation} />,
"Unknown error",
30000
);
}
}
const schema = z.object({
teamName: z.string({ required_error: "Team name is required" }),
});

const {
control,
handleSubmit,
formState: { isSubmitting },
} = useForm<{ teamName: string }>({
} = useForm<formFields>({
mode: "onSubmit",
resolver: zodResolver(schema),
});

const teamName = useController({
@@ -79,12 +50,34 @@ export function CreateTeamForm() {
});

return (
<form
onSubmit={handleSubmit(onValid, onInvalid)}
className={styles.createTeamForm}
<Form
control={control}
action="https://thunderstore.io/api/team/create"
method="post"
onSubmit={() => {
toast.addToast({ variant: "info", message: "Creating team" });
}}
onSuccess={(response) => {
console.log(response);
toast.addToast({ variant: "success", message: "Team created" });
}}
onError={(response) => {
if (isErrorResponse(response)) {
toast.addToast({
variant: "danger",
message: response.error.message,
duration: 30000,
});
} else {
// TODO: Add sentry error here
console.log("TODO: Sentry error logging missing!");
}
}}
validateStatus={(status) => status === 200}
className={styles.root}
>
<div className={styles.createTeamDialog}>
<div className={styles.createTeamDialogText}>
<div className={styles.dialog}>
<div className={styles.dialogText}>
Enter the name of the team you wish to create. Team names can contain
the characters a-z A-Z 0-9 _ and must not start or end with an _
</div>
@@ -111,7 +104,7 @@ export function CreateTeamForm() {
)}
</Button.Root>
</div>
</form>
</Form>
);
}

12 changes: 6 additions & 6 deletions packages/cyberstorm/src/components/TextInput/TextInput.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"use client";
import React, { ReactNode } from "react";
import React from "react";
import styles from "./TextInput.module.css";
import { Icon } from "../Icon/Icon";
import { classnames } from "../../utils/utils";

export interface TextInputProps {
children?: ReactNode;
export interface TextInputProps
extends React.ComponentPropsWithRef<"input">,
React.PropsWithChildren {
placeHolder?: string;
leftIcon?: JSX.Element;
id?: string;
@@ -14,9 +15,8 @@ export interface TextInputProps {
value?: string;
name?: string;
color?: string;
ref?: (instance: any) => void;
onBlur?: (val: any) => void;
onChange?: (val: any) => void;
onBlur?: React.FocusEventHandler<HTMLInputElement>;
onChange?: React.ChangeEventHandler<HTMLInputElement>;
enterHook?: (value: string) => string | void;
}

25 changes: 20 additions & 5 deletions packages/cyberstorm/src/components/Toast/Provider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { createContext, PropsWithChildren, useReducer } from "react";
import { v4 as uuid } from "uuid";
import * as RadixToast from "@radix-ui/react-toast";
import {
createContext,
PropsWithChildren,
useContext,
useReducer,
} from "react";
import { v4 as uuid } from "uuid";
import Toast from ".";
import { ToastProps } from "./Toast";

@@ -37,16 +42,16 @@ const toastReducer = (
};

interface ContextInterface {
addToast: (props: Omit<ToastProps, "id">) => void;
addToast: ({ ...props }: Omit<ToastProps, "id">) => void;
remove: (id: string) => void;
}

const ToastContext = createContext<ContextInterface | null>(null);
export const ToastContext = createContext<ContextInterface | null>(null);

export function Provider(props: { toastDuration: number } & PropsWithChildren) {
const [state, dispatch] = useReducer(toastReducer, initState);

const addToast = (props: Omit<ToastProps, "id">) => {
const addToast = ({ ...props }: Omit<ToastProps, "id">) => {
const id = uuid();
dispatch({ type: "add", toast: { id, ...props } });
};
@@ -69,3 +74,13 @@ export function Provider(props: { toastDuration: number } & PropsWithChildren) {
</ToastContext.Provider>
);
}

export const useToast = (): ContextInterface => {
const contextState = useContext(ToastContext);

if (contextState === null) {
throw new Error("useToast must be used within a Toast.Provider tag");
}

return contextState;
};
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -2588,6 +2588,11 @@
dependencies:
prop-types "^15.8.1"

"@hookform/resolvers@^3.3.2":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-3.3.2.tgz#5c40f06fe8137390b071d961c66d27ee8f76f3bc"
integrity sha512-Tw+GGPnBp+5DOsSg4ek3LCPgkBOuOgS5DsDV7qsWNH9LZc433kgsWICjlsh2J9p04H2K66hsXPPb9qn9ILdUtA==

"@humanwhocodes/config-array@^0.11.8":
version "0.11.8"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9"

0 comments on commit 02b4d89

Please sign in to comment.