Skip to content

Commit

Permalink
creating-multiples-is-some-fun (#136)
Browse files Browse the repository at this point in the history
* creating-multiples-is-some-fun

* fix update team resolver
  • Loading branch information
evboggs302 authored Jan 23, 2025
1 parent 1734781 commit 891766a
Show file tree
Hide file tree
Showing 28 changed files with 576 additions and 102 deletions.
10 changes: 7 additions & 3 deletions client/graphql.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2457,9 +2457,13 @@
"kind": "LIST",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
},
Expand Down
11 changes: 8 additions & 3 deletions client/src/features/clues/components/ClueCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { CluePayload } from "@generated/graphql";
import { useDeleteSingleClueMutation } from "../hooks/useDeleteSingleClueMutation";
import { useHuntFragment } from "@lib/hooks/useHuntFragment";
import { EditClueDialog } from "./EditClues/EditClueDialog";
// import { useDrag, useDrop } from "react-dnd";
// import CardMedia from "@mui/material/CardMedia";

type ClueCardProps = { clue: CluePayload };
Expand All @@ -20,7 +21,8 @@ export const ClueCard = ({
}: ClueCardProps) => {
const { hunt } = useHuntFragment();
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
const [deleteClue, { loading, error }] = useDeleteSingleClueMutation();
const [deleteClue, { loading: deleteLoading }] =
useDeleteSingleClueMutation();

const handleDelete = useCallback(async () => {
try {
Expand All @@ -32,7 +34,10 @@ export const ClueCard = ({

return (
<>
<Card sx={{ width: "380px", margin: "8px" }}>
<Card
// ref={dragRef}
sx={{ width: "380px", margin: "8px" }}
>
{/* <CardMedia
component="img"
alt="green iguana"
Expand All @@ -48,7 +53,7 @@ export const ClueCard = ({
</Typography>
</CardContent>
{!hunt.is_active && (
<CardActions>
<CardActions sx={{ display: deleteLoading ? "none" : undefined }}>
<Button size="small" onClick={handleDelete}>
Delete
</Button>
Expand Down
8 changes: 7 additions & 1 deletion client/src/features/clues/components/ClueCardList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { CardListContainer } from "@lib/components/Cards/CardListContainer";
import { ClueCard } from "./ClueCard";
import { useClueContext } from "@lib/context/ClueContext";
import { NoCardsToShowText } from "@lib/components/Cards/NoCardsToShowText";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";

export const ClueCardList = () => {
const { data } = useClueContext();
Expand All @@ -14,5 +16,9 @@ export const ClueCardList = () => {
return <NoCardsToShowText type="clues" />;
}

return <CardListContainer>{clueCards}</CardListContainer>;
return (
<DndProvider backend={HTML5Backend}>
<CardListContainer>{clueCards}</CardListContainer>
</DndProvider>
);
};

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import { useCallback, useEffect } from "react";
import { useFieldArray } from "react-hook-form";
import { useFieldArray, useFormContext } from "react-hook-form";
import Box from "@mui/material/Box";
import { FieldWrapper } from "@lib/components/Form/FieldWrapper";
import { MultiDescriptionField } from "./MultiDescriptionField";
import { AddAnotherClueBlutton } from "./AddAnotherClueBlutton";
import { useScrollShadow } from "@lib/hooks/useScrollShadow";
import Button from "@mui/material/Button";

export const MultipleCluesDialogContent = () => {
const { ref } = useScrollShadow();
const { fields, append, prepend, remove, swap, move, insert } = useFieldArray(
{
name: "cluesList",
}
);
const {
setFocus,
formState: { isValid, isSubmitting },
} = useFormContext();

const { fields, append, remove } = useFieldArray({
name: "cluesList",
});

const addAnother = useCallback(() => {
append("");
}, [append]);
setFocus(`cluesList[${fields.length}]`, { shouldSelect: true });
}, [append, fields.length, setFocus]);

useEffect(() => {
if (fields.length === 0) {
Expand All @@ -35,7 +39,9 @@ export const MultipleCluesDialogContent = () => {
/>
))}
</Box>
<AddAnotherClueBlutton onClick={addAnother} />
<Button disabled={!isValid || isSubmitting} onClick={addAnother}>
Add another clue
</Button>
</FieldWrapper>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,13 @@ export const EditClueDialog = ({
</FieldWrapper>
</DialogContent>
<DialogActions sx={{ display: "flex", justifyContent: "space-between" }}>
<Button onClick={handleClose}>Cancel</Button>
<Button disabled={loading} onClick={handleClose}>
Cancel
</Button>
<Box>
<Button
variant="outlined"
disabled={loading || field.value === description}
sx={{ marginRight: "4px" }}
onClick={() => reset()}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,17 @@ type CreateDialogProps = {
handleClose: () => void;
};

export type CreateTeamsFormState = CreateTeamsFormSchemaType & {
onSubmitError?: string;
};

export const CreateTeamsDialog = ({ handleClose }: CreateDialogProps) => {
const [resolver] = useTeamsResolver();
const [createSingle, singleTeamResult] = useCreateSingleTeamMutation();
const [createMultiple, mutlipleTeamsResult] =
useCreateMultipleTeamsMutation();
const [createSingle] = useCreateSingleTeamMutation();
const [createMultiple] = useCreateMultipleTeamsMutation();

const methods = useForm<CreateTeamsFormState>({
const methods = useForm<CreateTeamsFormSchemaType>({
mode: "onTouched",
resolver,
defaultValues: {
isMulti: false,
},
});

const {
Expand All @@ -54,17 +52,19 @@ export const CreateTeamsDialog = ({ handleClose }: CreateDialogProps) => {
handleSubmit,
formState: {
isValid,
isSubmitting,
errors: { onSubmitError },
},
} = methods;

const { field: toggleSwitch } = useController({
name: "isMulti",
control,
defaultValue: false,
});

const onSubmit: SubmitHandler<CreateTeamsFormState> = async (formData) => {
const onSubmit: SubmitHandler<CreateTeamsFormSchemaType> = async (
formData
) => {
clearErrors("onSubmitError");
await trigger();

Expand All @@ -88,7 +88,6 @@ export const CreateTeamsDialog = ({ handleClose }: CreateDialogProps) => {
}
};

const loading = singleTeamResult.loading || mutlipleTeamsResult.loading;
const toPluralizeTeam = +!toggleSwitch.value;

return (
Expand Down Expand Up @@ -124,21 +123,20 @@ export const CreateTeamsDialog = ({ handleClose }: CreateDialogProps) => {
value={toggleSwitch.value}
onBlur={toggleSwitch.onBlur}
onChange={toggleSwitch.onChange}
// disabled
/>
<InputLabel>
Creating multiple teams <i>(COMING SOON!)</i>
</InputLabel>
<InputLabel>Creating multiple teams</InputLabel>
</Box>
{toggleSwitch.value === false && <SingleTeamDialogContent />}
{toggleSwitch.value === true && <MultipleTeamsDialogContent />}
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button disabled={isSubmitting} onClick={handleClose}>
Cancel
</Button>
<Button
type="submit"
disabled={!isValid || loading}
endIcon={loading && <CircularProgress size={20} />}
disabled={!isValid || isSubmitting}
endIcon={isSubmitting && <CircularProgress size={20} />}
>
Create
</Button>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import Box from "@mui/material/Box";
import IconButton from "@mui/material/IconButton";
import TextField from "@mui/material/TextField";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
import { useController } from "react-hook-form";
import InputLabel from "@mui/material/InputLabel";
import { FieldWrapper } from "@lib/components/Form/FieldWrapper";

type MultiTeamFieldsProps = {
index: number;
remove: () => void;
};

export const MultiTeamsFields = ({ index, remove }: MultiTeamFieldsProps) => {
const { field: membersField, fieldState: membersFieldState } = useController({
name: `teams[${index}].members`,
defaultValue: "",
});

const { field: deviceField, fieldState: deviceFieldState } = useController({
name: `teams[${index}].device_number`,
defaultValue: "",
});

return (
<Box
sx={{
display: "flex",
flexDirection: "row",
margin: "5px",
alignItems: "center",
justifyContent: "space-between",
}}
>
<FieldWrapper sx={{ width: "100%" }}>
<InputLabel required>
Team members <i>(separated by a comma)</i>
</InputLabel>
<TextField
slotProps={{
htmlInput: {
"data-testid": `create-team-members-${index}`,
},
}}
multiline
inputRef={membersField.ref}
name={membersField.name}
value={membersField.value}
onBlur={membersField.onBlur}
onChange={membersField.onChange}
placeholder={`ie. 'Johnny, Sarah, Adam, Brittney'`}
error={!!membersFieldState.error}
helperText={
membersFieldState.error ? membersFieldState.error.message : null
}
color={membersFieldState.error ? "error" : "primary"}
fullWidth
variant="outlined"
/>
<InputLabel required>Cell phone number</InputLabel>
<TextField
slotProps={{
htmlInput: {
"data-testid": `create-team-device-number-${index}`,
},
}}
inputRef={deviceField.ref}
name={deviceField.name}
value={deviceField.value}
onBlur={deviceField.onBlur}
onChange={deviceField.onChange}
placeholder={`ie. +1-234-555-5678 or 2105551234`}
error={!!deviceFieldState.error}
helperText={
deviceFieldState.error ? deviceFieldState.error.message : null
}
color={deviceFieldState.error ? "error" : "primary"}
fullWidth
variant="outlined"
/>
</FieldWrapper>
<IconButton
onClick={remove}
sx={{ height: "max-content", marginLeft: "4px" }}
>
<DeleteOutlineIcon />
</IconButton>
</Box>
);
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,47 @@
import { useCallback, useEffect } from "react";
import { useFieldArray, useFormContext } from "react-hook-form";
import { FieldWrapper } from "@lib/components/Form/FieldWrapper";
import { useScrollShadow } from "@lib/hooks/useScrollShadow";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import { MultiTeamsFields } from "./MultiTeamsFields";

export const MultipleTeamsDialogContent = () => {
return <div>MultipleTeamsDialogContent</div>;
const { ref } = useScrollShadow();
const {
setFocus,
formState: { isSubmitting, isValid },
} = useFormContext();

const { fields, append, remove } = useFieldArray({
name: "teams",
});

const addAnother = useCallback(() => {
append({ members: "", device_number: "" });
setFocus(`teams[${fields.length + 1}]`, { shouldSelect: true });
}, [append, fields.length, setFocus]);

useEffect(() => {
if (fields.length === 0) {
addAnother();
}
}, [addAnother, fields]);

return (
<FieldWrapper>
<Box ref={ref} sx={{ maxHeight: "320px", overflowY: "auto" }}>
{fields.map(({ id }, index) => (
<MultiTeamsFields
key={id}
index={index}
remove={() => remove(index)}
/>
))}
</Box>
<Button disabled={!isValid || isSubmitting} onClick={addAnother}>
Add another team
</Button>
</FieldWrapper>
);
};
Loading

0 comments on commit 891766a

Please sign in to comment.