Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement design for list page #41

Merged
merged 3 commits into from
Oct 6, 2024
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
7 changes: 7 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
name="description"
content="A smart shopping list that learns your purchase habits and makes suggestions, so you don't forget to buy what's important."
/>
<link rel="icon" type="image/svg+xml" href="/src/favicon.ico" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap"
rel="stylesheet"
/>
<link rel="icon" type="image/svg+xml" href="/src/favicon-cart.png" />
<meta name="color-scheme" content="dark light" />
<title>GrocerEase</title>
Expand Down
42 changes: 42 additions & 0 deletions 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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"npm": ">=8.19.0"
},
"dependencies": {
"@radix-ui/react-alert-dialog": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-slot": "^1.1.0",
Expand Down
Binary file added public/img/underline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export function App() {
null,
);

const listName = listPath.split('/').pop();

/**
* This custom hook holds info about the current signed in user.
* Check ./api/useAuth.jsx for its implementation.
Expand Down Expand Up @@ -52,7 +54,9 @@ export function App() {
/>
<Route
path="/list"
element={<List data={data} listPath={listPath} />}
element={
<List data={data} listPath={listPath} listName={listName} />
}
/>
<Route
path="/manage-list"
Expand Down
86 changes: 65 additions & 21 deletions src/components/ListItem.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
import { updateItem, deleteItem } from '../api';
import { useEffect } from 'react';
import { useState, useEffect } from 'react';
import { ONE_DAY_IN_MILLISECONDS } from '../utils/dates';
import toast from 'react-hot-toast';
import { Button } from './ui/button';
import { Trash2 } from 'lucide-react';
import { getIndicatorColor } from '../utils/helpers';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from './ui/alert-dialog';

export function ListItem({
listPath,
Expand All @@ -16,6 +29,8 @@ export function ListItem({
dateCreated,
indicator,
}) {
const [isAlertOpen, setIsAlertOpen] = useState(false);

const handleOnChange = async (event) => {
let { checked } = event.target;
if (!checked) return;
Expand All @@ -30,13 +45,9 @@ export function ListItem({
};

const handleDelete = async () => {
const confirm = window.confirm(`are you sure you want to delete ${name}?`);
if (confirm) {
await deleteItem(listPath, id);
toast.success(`${name} was deleted from the list`);
} else {
toast.error('Deletion canceled');
}
await deleteItem(listPath, id);
toast.success(`${name} was deleted from the list`);
setIsAlertOpen(false);
};

useEffect(() => {
Expand All @@ -55,30 +66,63 @@ export function ListItem({
}, []);

return (
<li className="flex justify-between">
<div className="flex items-center gap-2">
<li className="flex flex-row items-center justify-between rounded-[5px] text-[1em] space-x-5 w-full bg-white text-black h-[3.3rem]">
<div className="flex items-center gap-3 ml-4">
<input
type="checkbox"
id={id}
onChange={handleOnChange}
checked={isChecked}
disabled={isChecked}
className="w-5 h-5 cursor-pointer"
/>
<div className="flex items-center gap-2">
<label htmlFor={`${id}`} className="font-medium text-lg">
{quantity}
</label>
<label htmlFor={`${id}`} className="font-medium text-lg">
<div
className={`flex items-center gap-2 ${isChecked ? 'line-through' : ''}`}
>
<label
htmlFor={`${id}`}
className="capitalize justify-self-end text-lg"
>
{name}
</label>
</div>
<div className="bg-pink w-6 h-6 flex items-center justify-center rounded-full">
<span className="font-bold text-xs">{quantity}</span>
</div>
</div>
{/* Add CSS to dynamically change bg-color for badges? */}
<div className="flex items-center gap-4">
<p className="">{indicator}</p>
<Button type="button" id={id} onClick={handleDelete}>
Delete
</Button>
<div className="flex items-center gap-2">
<div className={`${getIndicatorColor(indicator)} rounded-[5px] px-3`}>
<p className="capitalize justify-self-end text-base">{indicator}</p>
</div>
<AlertDialog open={isAlertOpen} onOpenChange={setIsAlertOpen}>
<AlertDialogTrigger asChild>
<Button
className="bg-transparent hover:bg-transparent"
type="button"
id={id}
onClick={() => setIsAlertOpen(true)}
>
<Trash2 className="text-main-green w-6 h-6 md:w-7 md:h-7" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. Do you really want to delete{' '}
{name}?
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={() => setIsAlertOpen(false)}>
Cancel
</AlertDialogCancel>
<AlertDialogAction onClick={handleDelete}>
Continue
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</li>
);
Expand Down
7 changes: 6 additions & 1 deletion src/components/SearchBar.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Input } from './ui/input';
import { ListFilter } from 'lucide-react';

export function SearchBar({ allData, setDisplayData, setSearch, search }) {
const handleInputChange = (e) => {
Expand All @@ -13,13 +14,17 @@ export function SearchBar({ allData, setDisplayData, setSearch, search }) {
};

return (
<form className="text-black">
<form className="relative w-full">
<span className="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<ListFilter className="h-5 w-5 text-grey" />
</span>
<Input
type="text"
id="item-filter"
value={search}
placeholder="Search..."
onChange={handleInputChange}
className="border-[1px] rounded-[5px] text-[1em] h-[3rem] pl-10 focus-visible:outline-none focus:ring-1 focus:ring-green-500 text-black"
/>
</form>
);
Expand Down
121 changes: 121 additions & 0 deletions src/components/ui/alert-dialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import * as React from 'react';
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';

import { cn } from '@/lib/utils';
import { buttonVariants } from '@/components/ui/button';

const AlertDialog = AlertDialogPrimitive.Root;

const AlertDialogTrigger = AlertDialogPrimitive.Trigger;

const AlertDialogPortal = AlertDialogPrimitive.Portal;

const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Overlay
className={cn(
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
className,
)}
{...props}
ref={ref}
/>
));
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;

const AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
ref={ref}
className={cn(
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-slate-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg dark:border-slate-800 dark:bg-slate-950',
className,
)}
{...props}
/>
</AlertDialogPortal>
));
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;

const AlertDialogHeader = ({ className, ...props }) => (
<div
className={cn(
'flex flex-col space-y-2 text-center sm:text-left',
className,
)}
{...props}
/>
);
AlertDialogHeader.displayName = 'AlertDialogHeader';

const AlertDialogFooter = ({ className, ...props }) => (
<div
className={cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
className,
)}
{...props}
/>
);
AlertDialogFooter.displayName = 'AlertDialogFooter';

const AlertDialogTitle = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Title
ref={ref}
className={cn('text-lg font-semibold', className)}
{...props}
/>
));
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;

const AlertDialogDescription = React.forwardRef(
({ className, ...props }, ref) => (
<AlertDialogPrimitive.Description
ref={ref}
className={cn('text-sm text-slate-500 dark:text-slate-400', className)}
{...props}
/>
),
);
AlertDialogDescription.displayName =
AlertDialogPrimitive.Description.displayName;

const AlertDialogAction = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Action
ref={ref}
className={cn(
buttonVariants(),
'bg-orange hover:bg-orange-hover text-black font-semibold',
className,
)}
{...props}
/>
));
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;

const AlertDialogCancel = React.forwardRef(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Cancel
ref={ref}
className={cn(
buttonVariants({ variant: 'outline' }),
'mt-2 sm:mt-0 text-black',
className,
)}
{...props}
/>
));
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;

export {
AlertDialog,
AlertDialogPortal,
AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
};
17 changes: 17 additions & 0 deletions src/utils/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,20 @@ export function getIndicator(item) {
return 'Not soon';
}
}

export const getIndicatorColor = (indicator) => {
switch (indicator) {
case 'Soon':
return 'bg-soon';
case 'Kind of soon':
return 'bg-kind-of-soon';
case 'Not soon':
return 'bg-not-soon';
case 'Inactive':
return 'bg-inactive';
case 'Overdue':
return 'bg-overdue';
default:
return 'bg-transparent';
}
};
Loading
Loading