Skip to content

Commit

Permalink
WIP: Add TeamDetails edit form
Browse files Browse the repository at this point in the history
  • Loading branch information
Oksamies committed Nov 28, 2023
1 parent c5b5ed4 commit 76a07b9
Show file tree
Hide file tree
Showing 12 changed files with 189 additions and 52 deletions.
10 changes: 9 additions & 1 deletion packages/cyberstorm-forms/src/components/FormTextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { z, ZodObject, ZodRawShape } from "zod";
import { Path, useController } from "react-hook-form";
import { TextInput } from "@thunderstore/cyberstorm";
import styles from "./FormTextInput.module.css";
import React from "react";

export type FormTextInputProps<
Schema extends ZodObject<Z>,
Expand All @@ -13,17 +14,24 @@ export type FormTextInputProps<
schema: Schema;
name: Path<z.infer<Schema>>;
placeholder?: string;
existingValue?: string;
};
export function FormTextInput<
Schema extends ZodObject<Z>,
Z extends ZodRawShape
>({ name, placeholder }: FormTextInputProps<Schema, Z>) {
>({ name, placeholder, existingValue }: FormTextInputProps<Schema, Z>) {
const {
field,
fieldState: { isDirty, invalid, error },
formState: { isSubmitting, disabled },
} = useController({ name });

if (existingValue) {
React.useEffect(() => {
field.onChange(existingValue);
}, [existingValue]);
}

return (
<>
<TextInput
Expand Down
65 changes: 65 additions & 0 deletions packages/cyberstorm-forms/src/forms/TeamDetailsEdit.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
.root {
display: flex;
flex-direction: column;
gap: 1rem;
}

.section {
display: flex;
flex-direction: column;
gap: var(--gap--32);
}

.donationLink {
display: flex;
align-items: center;
padding: var(--space--16) var(--space--32);
background-color: var(--color-surface--2);
}

.donationLinkLabel {
width: 8em;
font-weight: var(--font-weight-bold);
line-height: 1;
background-color: var(--color-surface--2);
}

.donationLinkActions {
display: flex;
flex: 1 1 0;
gap: var(--space--16);
align-items: center;
}

.donationLinkTextInput {
flex-grow: 1;
}

.donationLinkTextInput > div {
width: 100%;
}

.line {
margin: var(--space--24) 0;
border-bottom: var(--border-width--px) solid var(--color-surface--4);
}

.avatarContent {
display: flex;
flex-direction: row;
gap: var(--gap--32);
align-items: center;
}

.saveChangesWrapper {
display: flex;
flex-direction: row-reverse;
}

@media (max-width: 30rem) {
.donationLink {
flex-direction: column;
gap: var(--gap--16);
align-items: flex-start;
}
}
67 changes: 67 additions & 0 deletions packages/cyberstorm-forms/src/forms/TeamDetailsEdit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"use client";

import styles from "./TeamDetailsEdit.module.css";
import { teamDetailsEdit } from "@thunderstore/thunderstore-api";
import {
ApiForm,
teamDetailsEditFormSchema,
} from "@thunderstore/ts-api-react-forms";
import {
FormSubmitButton,
FormTextInput,
useFormToaster,
} from "@thunderstore/cyberstorm-forms";
import { useDapper } from "@thunderstore/dapper";
import { usePromise } from "@thunderstore/use-promise";
import { SettingItem } from "@thunderstore/cyberstorm/src/components/SettingItem/SettingItem";

export function TeamDetailsEdit({ teamName }: { teamName: string }) {
const toaster = useFormToaster({
successMessage: "Changes saved",
});

const dapper = useDapper();
const team = usePromise(dapper.getTeamDetails, [teamName]);

return (
<ApiForm
{...toaster}
schema={teamDetailsEditFormSchema}
metaData={{ identifier: team.name }}
endpoint={teamDetailsEdit}
formProps={{ className: styles.root }}
>
<div className={styles.root}>
<div className={styles.section}>
<SettingItem
title="Team donation link"
content={
<div className={styles.donationLink}>
<div className={styles.donationLinkLabel}>URL</div>
<div className={styles.donationLinkActions}>
<div className={styles.donationLinkTextInput}>
<FormTextInput
schema={teamDetailsEditFormSchema}
name={"donation_link"}
placeholder={"https://"}
existingValue={
team.donation_link === null
? undefined
: team.donation_link
}
/>
</div>
</div>
</div>
}
/>
</div>
<div className={styles.saveChangesWrapper}>
<FormSubmitButton text="Save changes" />
</div>
</div>
</ApiForm>
);
}

TeamDetailsEdit.displayName = "TeamDetailsEdit";
1 change: 1 addition & 0 deletions packages/cyberstorm-forms/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { FormMultiSelectSearch } from "./components/FormMultiSelectSearch";
export { FormSwitch } from "./components/FormSwitch";
export { FormTextInput } from "./components/FormTextInput";
export { CreateTeamForm } from "./forms/CreateTeamForm";
export { TeamDetailsEdit } from "./forms/TeamDetailsEdit";
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
import { useDapper } from "@thunderstore/dapper";
import { usePromise } from "@thunderstore/use-promise";
import styles from "./TeamDetails.module.css";
import { SettingItem } from "../../../../SettingItem/SettingItem";
import { TextInput } from "../../../../TextInput/TextInput";
import * as Button from "../../../../Button/";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useState } from "react";
import { faXmarkLarge } from "@fortawesome/pro-solid-svg-icons";
import { TeamDetailsEdit } from "@thunderstore/cyberstorm-forms";

interface Props {
teamName: string;
Expand All @@ -15,42 +7,7 @@ interface Props {
export function TeamDetails(props: Props) {
const { teamName } = props;

const dapper = useDapper();
const team = usePromise(dapper.getTeamDetails, [teamName]);

const [donationLink, setDonationLink] = useState(team.donation_link ?? "");

return (
<div className={styles.root}>
<div className={styles.section}>
<SettingItem
title="Team donation link"
content={
<div className={styles.donationLink}>
<div className={styles.donationLinkLabel}>URL</div>
<div className={styles.donationLinkActions}>
<div className={styles.donationLinkTextInput}>
<TextInput
placeholder="https://"
onChange={(e) => setDonationLink(e.target.value)}
value={donationLink}
/>
</div>
<Button.Root
paddingSize="mediumSquare"
colorScheme="transparentDanger"
>
<Button.ButtonIcon>
<FontAwesomeIcon icon={faXmarkLarge} />
</Button.ButtonIcon>
</Button.Root>
</div>
</div>
}
/>
</div>
</div>
);
return <TeamDetailsEdit teamName={teamName} />;

/*
return (
Expand Down
27 changes: 27 additions & 0 deletions packages/thunderstore-api/src/fetch/teamDetailsEdit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { RequestConfig } from "../index";
import { apiFetch2 } from "../apiFetch";

export type teamDetailsEditMetaData = {
identifier: string;
};

export type teamDetailsEditApiArgs = {
donation_link: string;
};

export function teamDetailsEdit(
config: RequestConfig,
data: teamDetailsEditApiArgs,
metaData: teamDetailsEditMetaData
) {
const path = `api/cyberstorm/team/${metaData.identifier}/edit/`;

return apiFetch2({
config,
path,
request: {
method: "POST",
body: JSON.stringify(data),
},
});
}
1 change: 1 addition & 0 deletions packages/thunderstore-api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export * from "./fetch/teamDetails";
export * from "./fetch/teamMembers";
export * from "./fetch/teamServiceAccounts";
export * from "./fetch/teamCreate";
export * from "./fetch/teamDetailsEdit";
export * from "./errors";
4 changes: 3 additions & 1 deletion packages/ts-api-react-forms/src/ApiForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type ApiFormProps<
> = {
schema: Schema;
endpoint: ApiEndpoint<z.infer<Schema>, Result>;
metaData: any;
onSubmitSuccess?: (result: Result) => void;
onSubmitError?: (error: Error | ApiError | unknown) => void;
formProps?: Omit<HTMLAttributes<HTMLFormElement>, "onSubmit">;
Expand All @@ -23,9 +24,10 @@ export function ApiForm<
Result extends object,
Z extends ZodRawShape
>(props: PropsWithChildren<ApiFormProps<Schema, Result, Z>>) {
const { schema, endpoint, onSubmitSuccess, onSubmitError } = props;
const { schema, metaData, endpoint, onSubmitSuccess, onSubmitError } = props;
const { form, submitHandler } = useApiForm({
schema: schema,
metaData: metaData,
endpoint: endpoint,
});
const onSubmit = useCallback(
Expand Down
1 change: 1 addition & 0 deletions packages/ts-api-react-forms/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export { getErrorFormKey, handleFormApiErrors } from "./errors";
export { useApiForm } from "./useApiForm";
export type { UseApiFormReturn, UseApiFormArgs } from "./useApiForm";
export { createTeamFormSchema } from "./schema";
export { teamDetailsEditFormSchema } from "./schema";
6 changes: 6 additions & 0 deletions packages/ts-api-react-forms/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ export const createTeamFormSchema = z.object({
.string({ required_error: "Team name is required" })
.min(1, { message: "Team name is required" }),
});

export const teamDetailsEditFormSchema = z.object({
donation_link: z
.string({ required_error: "Donation link is required" })
.min(1, { message: "Donation link is required" }),
});
5 changes: 3 additions & 2 deletions packages/ts-api-react-forms/src/useApiForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type UseApiFormArgs<
Z extends ZodRawShape
> = {
schema: Schema;
metaData: any;
endpoint: ApiEndpoint<z.infer<Schema>, Result>;
};
export type UseApiFormReturn<
Expand All @@ -29,7 +30,7 @@ export function useApiForm<
>(
args: UseApiFormArgs<Schema, Result, Z>
): UseApiFormReturn<Schema, Result, Z> {
const { schema, endpoint } = args;
const { schema, metaData, endpoint } = args;
const apiCall = useApiCall(endpoint);

const form = useForm<z.infer<Schema>>({
Expand All @@ -38,7 +39,7 @@ export function useApiForm<
});
const submitHandler = async (data: z.infer<Schema>) => {
try {
return await apiCall(data);
return await apiCall(data, metaData);
} catch (e) {
handleFormApiErrors(e, schema, form.setError);
throw e;
Expand Down
7 changes: 4 additions & 3 deletions packages/ts-api-react/src/useApiCall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { useApiConfig } from "./useApiConfig";

export type ApiEndpoint<Data, Result> = (
config: RequestConfig,
data: Data
data: Data,
metaData: any
) => Promise<Result>;
export function useApiCall<Data, Result>(
endpoint: ApiEndpoint<Data, Result>
): (data: Data) => Promise<Result> {
): (data: Data, metaData: any) => Promise<Result> {
const apiConfig = useApiConfig();
return (data: Data) => endpoint(apiConfig, data);
return (data: Data, metaData: any) => endpoint(apiConfig, data, metaData);
}

0 comments on commit 76a07b9

Please sign in to comment.