Skip to content

Commit

Permalink
Section for trackables (#111)
Browse files Browse the repository at this point in the history
  • Loading branch information
jarrod-lowe authored Sep 21, 2024
1 parent 51d2e68 commit ce4459f
Show file tree
Hide file tree
Showing 8 changed files with 609 additions and 2 deletions.
145 changes: 145 additions & 0 deletions design/EDGES-SECTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Advanced Section

## Current State

In my app, I have player sheets with multiple sections of different types. Below
is the code for a textbox section. It has two modes - display and edit. In
display mode, it shows the name and content, and in edit mode we can change the
name and content.

## New Section

I want to add a new section type - called "Trackable". It will have a name, which works the same as in the Text type. The content JSON should look like:

```json
{
"showZeros": true,
"items": [
{
"name": "Something",
"length": 3,
"ticked": 0,
"dscription": "A long description about the item."
},
...
]
}
```

In display mode, it should show, in tidy columns that fit the page, a block for each item in items formatted like: `Something [ ] [ ] [ ] (i)`. Those `[ ]` are checkboxes. The checkboxes should be positioned so that every item takes the same amount of space - and so that scanning down multiple of them in the same row, you'll see them all lined up. If `ticked` had been 2, then the first two would be ticked. If the user clicks on one that is ticked, I want to decrement ticked count and render with one less checkbox ticked. If an empty one is clicked, then add one to `ticked`. No matter which was selected, the ticked ones start from the left-most box for LTR languages, of the right-most for RTL languages. The `(i)` is an information icon, and if hovered over or clicked, should show the description. Showing the description should not re-arrange any of the other content on the page. The description should be dismissable. If "showZeros" is false, then any item with `ticked` equal to zero should not be shown, and should not be able to be changed to have less than one ticked.

In edit mode, It should have only one column of items. The name should be editable. There should be a +/- for the length, and there should be an editable textbox for the description. If the length is reduced below the number ticked, the number ticked should also be reduced. You cannot have a negative length, or greater than 10. There should also be a tick-box (one; not per-item), for "Show entries with no ticks" that the user can toggle.

In edit mode, the user should also be able to completely remove any item, and to add new ones.

## Existing Considerations

Please make sure to use the same internationalisation system, and general techniques, as the `sectionText.tsx` shown below.

## Existing Code

```typescript
import React, { useState } from 'react';
import { generateClient } from "aws-amplify/api";
import { SheetSection, UpdateSectionInput } from "../../appsync/graphql";
import { updateSectionMutation } from "../../appsync/schema";
import { FormattedMessage, useIntl } from 'react-intl';
import { GraphQLResult } from "@aws-amplify/api-graphql";
import { FaPencilAlt } from 'react-icons/fa';
import { useToast } from './notificationToast';

type SectionTypeText = {
text: string;
};

// Section component
export const SectionText: React.FC<{ section: SheetSection, userSubject: string, onUpdate: (updatedSection: SheetSection) => void }> = ({ section, userSubject, onUpdate }) => {
const [isEditing, setIsEditing] = useState(false);
const [content, setContent] = useState(JSON.parse(section.content) as SectionTypeText);
const [sectionName, setSectionName] = useState(section.sectionName);
const [originalContent, setOriginalContent] = useState(content);
const [originalSectionName, setOriginalSectionName] = useState(section.sectionName);
const intl = useIntl(); // Get the intl object for translation
const toast = useToast();

const handleUpdate = async () => {
try {
const input: UpdateSectionInput = {
gameId: section.gameId,
sectionId: section.sectionId,
sectionName: sectionName,
sectionType: section.sectionType,
content: JSON.stringify(content),
}
const client = generateClient();
const response = await client.graphql({
query: updateSectionMutation,
variables: {
input: input,
}
}) as GraphQLResult<{ updateSection: SheetSection }>;
onUpdate(response.data.updateSection);
setIsEditing(false);
} catch (error) {
console.error("Error updating section:", error);
toast.addToast(intl.formatMessage({ id: "sectionText.updateError" }), 'error');

}
};

const handleCancel = () => {
// Reset the content and section name to their original values
setContent(originalContent);
setSectionName(originalSectionName);
setIsEditing(false);
};

if (userSubject !== section.userId) {
return (
<div className="section">
<h3>{sectionName}</h3>
<p>{content.text}</p>
</div>
);
}

if (isEditing) {
return (
<div className="section">
<input
type="text"
value={sectionName}
onChange={(e) => setSectionName(e.target.value)}
placeholder={intl.formatMessage({ id: "sectionName" })}
/>
<textarea
value={content.text}
onChange={(e) => setContent({ ...content, text: e.target.value })}
placeholder={intl.formatMessage({ id: "sectionText.sampleContent" })}
/>
<button onClick={handleUpdate}>
<FormattedMessage id="save" />
</button>
<button onClick={handleCancel}>
<FormattedMessage id="cancel" />
</button>
</div>
);
}

return (
<div className="section">
<h3>{sectionName} <FaPencilAlt onClick={() => {
setOriginalContent(content);
setOriginalSectionName(section.sectionName);
setIsEditing(true);
}} /></h3>
<p>{content.text}</p>
</div>
);
};
```

## Your Task

Please write the code for `sectionTrackable.tsx`.
84 changes: 83 additions & 1 deletion ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"react-icons": "5.3.0",
"react-intl": "6.6.8",
"react-router-dom": "6.26.2",
"react-tooltip": "5.28.0",
"uuid": "10.0.0"
},
"scripts": {
Expand Down
64 changes: 64 additions & 0 deletions ui/public/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,67 @@ button:hover {
transform: translateY(0);
}
}

.trackable-item {
display: flex;
align-items: center;
margin-bottom: 0.5rem;
}

.trackable-item span {
flex-grow: 1;
}

.trackable-item input[type="checkbox"] {
margin-right: 0.5rem;
}

.info-icon {
margin-left: 0.5rem;
cursor: pointer;
}

.trackable-item-edit {
display: flex;
align-items: center;
margin-bottom: 0.5rem;
}

.trackable-item-edit input[type="text"],
.trackable-item-edit textarea {
margin-right: 1rem;
}

.item-length-controls {
display: flex;
align-items: center;
}

.item-length-controls button {
width: 2rem;
height: 2rem;
margin: 0 0.5rem;
}

.show-zeros-toggle {
margin-top: 1rem;
}

.trackable-items {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
}

.trackable-items-edit {
display: flex;
flex-direction: column;
}

.react-tooltip {
max-width: 250px; /* Adjust to the width you prefer */
white-space: normal; /* Allow text to wrap */
word-wrap: break-word; /* Ensure long words are broken if needed */
text-align: left; /* Align text left for readability */
padding: 10px; /* Optional: Adds padding around the text */
}
2 changes: 2 additions & 0 deletions ui/src/sectionRegistry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { SheetSection } from "../../appsync/graphql";
import { SectionText } from './sectionText';
import { SectionNumber } from './sectionNumber';
import { SectionTrackable } from './sectionTrackable';

type SectionTypeConfig = {
component: React.FC<{ section: SheetSection, userSubject: string, onUpdate: (updatedSection: SheetSection) => void }>;
Expand All @@ -12,6 +13,7 @@ type SectionTypeConfig = {
const sectionRegistry: Record<string, SectionTypeConfig> = {
'TEXT': { component: SectionText, label: 'sectionType.text' },
'NUMBER': { component: SectionNumber, label: 'sectionType.number' },
'TRACKABLE': { component: SectionTrackable, label: 'sectionType.trackable' }
};

// Function to get the component for a section type
Expand Down
Loading

0 comments on commit ce4459f

Please sign in to comment.