Skip to content

Commit

Permalink
Merge pull request #37 from the-collab-lab/tg/list-path
Browse files Browse the repository at this point in the history
Managing `listPath`'s impossible null state
  • Loading branch information
tannaurus authored Sep 27, 2024
2 parents aec46aa + 74b3e38 commit 76e18e4
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 103 deletions.
7 changes: 1 addition & 6 deletions src/components/ListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { moreThan24HoursPassed, getDaysBetweenDates } from "../utils";

interface Props {
item: ListItem;
listPath: string | null;
listPath: string;
}
interface None {
kind: "none";
Expand Down Expand Up @@ -66,11 +66,6 @@ export function ListItemCheckBox({ item, listPath }: Props) {
// Temporarily store the updated check state
setUpdatedCheckState({ kind: "set", value: newCheckedState });

if (!listPath) {
toast.error("Error: listPath is missing or invalid.");
return;
}

try {
await toast.promise(updateItem(listPath, item), {
loading: `Marking ${item.name} as purchased!`,
Expand Down
145 changes: 68 additions & 77 deletions src/components/forms/AddItemForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import toast from "react-hot-toast";
import { useNavigate } from "react-router-dom";

interface Props {
listPath: string | null;
listPath: string;
data: ListItem[];
}

Expand Down Expand Up @@ -90,82 +90,73 @@ export function AddItemForm({ listPath, data: unfilteredListItems }: Props) {

return (
<section>
{listPath && (
<>
<form onSubmit={(e) => handleSubmit(e, listPath)}>
<h3>First, add your item!</h3>
<label htmlFor="item-name">
Item:
<input
id="item-name"
type="text"
name="item"
value={itemName}
onChange={handleItemNameTextChange}
aria-label="Enter the item name"
aria-required
/>
</label>
<br />
<h3>Next, pick when you plan on buying this item again!</h3>
<fieldset>
<legend>When to buy:</legend>
<label htmlFor={PurchaseTime.soon}>
<input
type="radio"
id={PurchaseTime.soon}
name="when-to-buy"
value={PurchaseTime.soon}
required
onChange={() => handleNextPurchaseChange(PurchaseTime.soon)}
checked={itemNextPurchaseTimeline === PurchaseTime.soon}
aria-label={`Set buy to soon, within ${purchaseTimelines[PurchaseTime.soon]} days`}
/>
Soon -- Within {purchaseTimelines[PurchaseTime.soon]} days!
</label>
<br />
<label htmlFor={PurchaseTime.kindOfSoon}>
<input
type="radio"
id={PurchaseTime.kindOfSoon}
name="when-to-buy"
value={PurchaseTime.kindOfSoon}
required
onChange={() =>
handleNextPurchaseChange(PurchaseTime.kindOfSoon)
}
checked={itemNextPurchaseTimeline === PurchaseTime.kindOfSoon}
aria-label={`Set buy to kind of soon, within ${purchaseTimelines[PurchaseTime.kindOfSoon]} days`}
/>
Kind of soon -- Within{" "}
{purchaseTimelines[PurchaseTime.kindOfSoon]} days!
</label>
<br />
<label htmlFor={PurchaseTime.notSoon}>
<input
type="radio"
id={PurchaseTime.notSoon}
name="when-to-buy"
value={PurchaseTime.notSoon}
required
onChange={() =>
handleNextPurchaseChange(PurchaseTime.notSoon)
}
checked={itemNextPurchaseTimeline === PurchaseTime.notSoon}
aria-label={`Set buy to not soon, within ${purchaseTimelines[PurchaseTime.notSoon]} days`}
/>
Not soon -- Within {purchaseTimelines[PurchaseTime.notSoon]}{" "}
days!
</label>
</fieldset>
<button type="submit" aria-label="Add item to shopping list">
Submit Item
</button>
</form>
<h4>Let&apos;s go look at your list!</h4>
<button onClick={navigateToListPage}>{"View List"}</button>
</>
)}
<form onSubmit={(e) => handleSubmit(e, listPath)}>
<h3>First, add your item!</h3>
<label htmlFor="item-name">
Item:
<input
id="item-name"
type="text"
name="item"
value={itemName}
onChange={handleItemNameTextChange}
aria-label="Enter the item name"
aria-required
/>
</label>
<br />
<h3>Next, pick when you plan on buying this item again!</h3>
<fieldset>
<legend>When to buy:</legend>
<label htmlFor={PurchaseTime.soon}>
<input
type="radio"
id={PurchaseTime.soon}
name="when-to-buy"
value={PurchaseTime.soon}
required
onChange={() => handleNextPurchaseChange(PurchaseTime.soon)}
checked={itemNextPurchaseTimeline === PurchaseTime.soon}
aria-label={`Set buy to soon, within ${purchaseTimelines[PurchaseTime.soon]} days`}
/>
Soon -- Within {purchaseTimelines[PurchaseTime.soon]} days!
</label>
<br />
<label htmlFor={PurchaseTime.kindOfSoon}>
<input
type="radio"
id={PurchaseTime.kindOfSoon}
name="when-to-buy"
value={PurchaseTime.kindOfSoon}
required
onChange={() => handleNextPurchaseChange(PurchaseTime.kindOfSoon)}
checked={itemNextPurchaseTimeline === PurchaseTime.kindOfSoon}
aria-label={`Set buy to kind of soon, within ${purchaseTimelines[PurchaseTime.kindOfSoon]} days`}
/>
Kind of soon -- Within {purchaseTimelines[PurchaseTime.kindOfSoon]}{" "}
days!
</label>
<br />
<label htmlFor={PurchaseTime.notSoon}>
<input
type="radio"
id={PurchaseTime.notSoon}
name="when-to-buy"
value={PurchaseTime.notSoon}
required
onChange={() => handleNextPurchaseChange(PurchaseTime.notSoon)}
checked={itemNextPurchaseTimeline === PurchaseTime.notSoon}
aria-label={`Set buy to not soon, within ${purchaseTimelines[PurchaseTime.notSoon]} days`}
/>
Not soon -- Within {purchaseTimelines[PurchaseTime.notSoon]} days!
</label>
</fieldset>
<button type="submit" aria-label="Add item to shopping list">
Submit Item
</button>
</form>
<h4>Let&apos;s go look at your list!</h4>
<button onClick={navigateToListPage}>{"View List"}</button>
</section>
);
}
8 changes: 2 additions & 6 deletions src/components/forms/ShareListForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getUser } from "../ProtectedRoute";
import toast from "react-hot-toast";

interface Props {
listPath: string | null;
listPath: string;
}

const ShareListForm = ({ listPath }: Props) => {
Expand All @@ -19,14 +19,10 @@ const ShareListForm = ({ listPath }: Props) => {

const handleInvite = async (
e: FormEvent<HTMLFormElement>,
listPath: string | null,
listPath: string,
) => {
e.preventDefault();

if (!listPath) {
return;
}

try {
await toast.promise(shareList(listPath, currentUser, emailName), {
loading: "sharing list with existing user",
Expand Down
8 changes: 4 additions & 4 deletions src/utils/validateTrimmedString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ export function validateItemName(
input: string,
existingItems: ListItem[],
): string | null {
const trimmedInput = input.trim(); //removes leading and trailing whitespaces
const trimmedInput = input.trim(); // removes leading and trailing white spaces

// Condition 1: Check if the input is empty
if (trimmedInput.length === 0) {
return "Item cannot be empty";
}

//Remove punctuation marks and normalize input
// Remove punctuation marks and normalize input
const punctuationRegex = /[^\p{L}]/gu;

const normalizedInputName = trimmedInput
.replace(punctuationRegex, "")
.toLowerCase();

//Create a list of normalized existing item names
// Create a list of normalized existing item names
const normalizedExistingItemNames = existingItems.map((existingItem) => {
return existingItem.name.replace(punctuationRegex, "").toLowerCase();
});
Expand All @@ -31,7 +31,7 @@ export function validateItemName(
);
};

//return error if the item already exists
// Return error if the item already exists
if (isDuplicateItem(normalizedInputName)) {
return ` ${normalizedInputName} already exists in the list`;
}
Expand Down
22 changes: 14 additions & 8 deletions src/views/authenticated/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,24 @@ export function List({ data: unfilteredListItems, listPath }: Props) {
.sort(comparePurchaseUrgency);
}, [searchTerm, unfilteredListItems]);

const Header = () => {
return (
<p>
Hello from the <code>/list</code> page!
</p>
);
};

if (!listPath) {
return <Header />;
}

// Early return if the list is empty
if (unfilteredListItems.length === 0) {
return (
<>
<p>
Hello from the <code>/list</code> page!
</p>
<Header />
<section>
<h2>Your list is ready!</h2>
<h3>
You haven’t added any items yet.
<br />
Expand All @@ -49,10 +58,7 @@ export function List({ data: unfilteredListItems, listPath }: Props) {
// Main content when list is not empty
return (
<>
<p>
Hello from the <code>/list</code> page!
</p>

<Header />
<div>
<section>
{unfilteredListItems.length > 0 && (
Expand Down
14 changes: 12 additions & 2 deletions src/views/authenticated/ManageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,21 @@ interface Props {
}

export function ManageList({ listPath, data }: Props) {
return (
<div>
const Header = () => {
return (
<p>
Hello from the <code>/manage-list</code> page!
</p>
);
};

if (!listPath) {
return <Header />;
}

return (
<div>
<Header />
<AddItemForm listPath={listPath} data={data || []} />
<ShareListForm listPath={listPath} />
</div>
Expand Down

0 comments on commit 76e18e4

Please sign in to comment.