Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions src/goals/CharacterInventory/CharInv/CharacterDetail/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Close } from "@mui/icons-material";
import { Grid, IconButton, Typography } from "@mui/material";
import { Grid2, IconButton, Typography } from "@mui/material";
import { ReactElement } from "react";

import CharacterInfo from "goals/CharacterInventory/CharInv/CharacterDetail/CharacterInfo";
Expand All @@ -18,39 +18,39 @@ export default function CharacterDetail(
props: CharacterDetailProps
): ReactElement {
return (
<Grid
<Grid2
container
spacing={2}
direction="row"
justifyContent="flex-start"
alignItems="center"
style={{ padding: theme.spacing(1) }}
>
<Grid item xs={3}>
<Grid2 size={3}>
<Typography variant="h1" align="center">
{props.character}
{""}
{/* There is a zero-width joiner here in case of non-printing characters. */}
</Typography>
</Grid>
<Grid item xs={8}>
</Grid2>
<Grid2 size={8}>
<CharacterStatusControl character={props.character} />
</Grid>
<Grid item xs={1}>
</Grid2>
<Grid2 size={1}>
<IconButton onClick={() => props.close()} size="large">
{" "}
<Close />
</IconButton>
</Grid>
<Grid item xs={12}>
</Grid2>
<Grid2 size={12}>
<CharacterInfo character={props.character} />
</Grid>
<Grid item xs={12}>
</Grid2>
<Grid2 size={12}>
<CharacterWords character={props.character} />
</Grid>
<Grid item xs={12}>
</Grid2>
<Grid2 size={12}>
<FindAndReplace initialFindValue={props.character} />
</Grid>
</Grid>
</Grid2>
</Grid2>
);
}
199 changes: 153 additions & 46 deletions src/goals/CharacterInventory/CharInv/CharacterEntry.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
import { KeyboardArrowDown } from "@mui/icons-material";
import { Button, Collapse, Grid } from "@mui/material";
import {
Button,
Collapse,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
Grid2,
} from "@mui/material";
import { ReactElement, ReactNode, useState } from "react";
import { useTranslation } from "react-i18next";

import { LoadingButton } from "components/Buttons";
import {
exit,
setRejectedCharacters,
setValidCharacters,
uploadInventory,
} from "goals/CharacterInventory/Redux/CharacterInventoryActions";
import { useAppDispatch, useAppSelector } from "rootRedux/hooks";
import { type StoreState } from "rootRedux/types";
import theme from "types/theme";
import { TextFieldWithFont } from "utilities/fontComponents";

export enum CharInvCancelSaveIds {
ButtonCancel = "char-inv-cancel-button",
ButtonSave = "char-inv-save-button",
DialogCancel = "char-inv-cancel-dialog",
DialogCancelButtonNo = "char-inv-cancel-dialog-no-button",
DialogCancelButtonYes = "char-inv-cancel-dialog-yes-button",
}

/**
* Allows for viewing and entering accepted and rejected characters in a
* character set
Expand All @@ -23,56 +43,91 @@ export default function CharacterEntry(): ReactElement {
(state: StoreState) => state.characterInventoryState
);

const [checked, setChecked] = useState(false);
const [advancedOpen, setAdvancedOpen] = useState(false);
const [cancelDialogOpen, setCancelDialogOpen] = useState(false);
const [saveInProgress, setSaveInProgress] = useState(false);

const { t } = useTranslation();

const save = async (): Promise<void> => {
setSaveInProgress(true);
await dispatch(uploadInventory());
};

return (
<Grid item xs={12}>
<Grid
container
style={{
background: "whitesmoke",
borderTop: "1px solid #ccc",
padding: theme.spacing(1),
}}
spacing={2}
>
<Button
id="character-entry-submit"
onClick={() => setChecked(!checked)}
>
{t("charInventory.characterSet.advanced")}{" "}
<KeyboardArrowDown
style={{
transform: checked ? "rotate(180deg)" : "rotate(0deg)",
transition: "all 200ms",
<div
style={{
background: "whitesmoke",
border: "1px solid #ccc",
padding: theme.spacing(1),
}}
>
<Grid2 container alignContent="center" justifyContent="space-between">
<Grid2 container spacing={2}>
{/* Save button */}
<LoadingButton
buttonProps={{
"data-testid": CharInvCancelSaveIds.ButtonSave,
id: CharInvCancelSaveIds.ButtonSave,
onClick: () => save(),
}}
loading={saveInProgress}
>
{t("buttons.save")}
</LoadingButton>

{/* Cancel button */}
<Button
color="secondary"
data-testid={CharInvCancelSaveIds.ButtonCancel}
id={CharInvCancelSaveIds.ButtonCancel}
onClick={() => setCancelDialogOpen(true)}
variant="contained"
>
{t("buttons.cancel")}
</Button>

{/* Cancel yes/no dialog */}
<CancelDialog
onClose={() => setCancelDialogOpen(false)}
open={cancelDialogOpen}
/>
</Button>
<Collapse in={checked} style={{ width: "100%" }}>
{/* Input for accepted characters */}
<Grid item xs={12}>
<CharactersInput
characters={validCharacters}
id="valid-characters-input"
label={t("charInventory.characterSet.acceptedCharacters")}
setCharacters={(chars) => dispatch(setValidCharacters(chars))}
/>
</Grid>

{/* Input for rejected characters */}
<Grid item xs={12}>
<CharactersInput
characters={rejectedCharacters}
id="rejected-characters-input"
label={t("charInventory.characterSet.rejectedCharacters")}
setCharacters={(chars) => dispatch(setRejectedCharacters(chars))}
</Grid2>

{/* Advanced toggle-button */}
<Button
endIcon={
<KeyboardArrowDown
style={{
transform: advancedOpen ? "rotate(180deg)" : "rotate(0deg)",
transition: "all 200ms",
}}
/>
</Grid>
</Collapse>
</Grid>
</Grid>
}
onClick={() => setAdvancedOpen((prev) => !prev)}
>
{t("charInventory.characterSet.advanced")}
</Button>
</Grid2>

<Collapse in={advancedOpen}>
{/* Input for accepted characters */}
<CharactersInput
characters={validCharacters}
id="valid-characters-input"
label={t("charInventory.characterSet.acceptedCharacters")}
setCharacters={(chars) => dispatch(setValidCharacters(chars))}
/>

{/* Input for rejected characters */}
<CharactersInput
characters={rejectedCharacters}
id="rejected-characters-input"
label={t("charInventory.characterSet.rejectedCharacters")}
setCharacters={(chars) => dispatch(setRejectedCharacters(chars))}
/>
</Collapse>
</div>
);
}

Expand All @@ -89,16 +144,68 @@ function CharactersInput(props: CharactersInputProps): ReactElement {
autoComplete="off"
fullWidth
id={props.id}
inputProps={{ spellCheck: false, style: { letterSpacing: 5 } }}
inputProps={{
"data-testid": props.id,
spellCheck: false,
style: { letterSpacing: 5 },
}}
label={props.label}
name="characters"
onChange={(e) =>
props.setCharacters(e.target.value.replace(/\s/g, "").split(""))
}
style={{ maxWidth: 512, marginTop: theme.spacing(1) }}
style={{ marginTop: theme.spacing(2) }}
value={props.characters.join("")}
variant="outlined"
vernacular
/>
);
}

interface CancelDialogProps {
open: boolean;
onClose: () => void;
}

/** "Are you sure?" dialog for the cancel button */
function CancelDialog(props: CancelDialogProps): ReactElement {
const { t } = useTranslation();

return (
<Dialog
data-testid={CharInvCancelSaveIds.DialogCancel}
id={CharInvCancelSaveIds.DialogCancel}
onClose={() => props.onClose()}
open={props.open}
>
<DialogTitle>{t("charInventory.dialog.title")}</DialogTitle>

<DialogContent>
<DialogContentText>
{t("charInventory.dialog.content")}
</DialogContentText>
</DialogContent>

<DialogActions>
<Button
autoFocus
color="secondary"
data-testid={CharInvCancelSaveIds.DialogCancelButtonYes}
id={CharInvCancelSaveIds.DialogCancelButtonYes}
onClick={() => exit()}
variant="contained"
>
{t("charInventory.dialog.yes")}
</Button>

<Button
data-testid={CharInvCancelSaveIds.DialogCancelButtonNo}
id={CharInvCancelSaveIds.DialogCancelButtonNo}
onClick={() => props.onClose()}
>
{t("charInventory.dialog.no")}
</Button>
</DialogActions>
</Dialog>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { type ReactElement } from "react";
import { useTranslation } from "react-i18next";

import { CharacterStatus } from "goals/CharacterInventory/CharacterInventoryTypes";
import { themeColors } from "types/theme";

interface CharacterStatusTextProps {
status: CharacterStatus;
Expand All @@ -17,24 +16,22 @@ export default function CharacterStatusText(

return (
<Typography
variant="body2"
color="textSecondary"
component="p"
style={CharacterStatusStyle(props.status)}
display={props.inline ? "inline" : "initial"}
sx={{ color: CharStatusColor(props.status) }}
variant="body2"
>
{t(`buttons.${props.status}`)}
</Typography>
);
}

function CharacterStatusStyle(status: CharacterStatus): { color: string } {
function CharStatusColor(status: CharacterStatus): string {
switch (status) {
case CharacterStatus.Accepted:
return { color: themeColors.success };
return "success.main";
case CharacterStatus.Rejected:
return { color: themeColors.error };
return "error.main";
case CharacterStatus.Undecided:
return { color: themeColors.primary };
return "primary.main";
}
}
Loading
Loading