Skip to content

Commit

Permalink
Merge pull request #18 from saleem-hadad/feat/move-to-searchable-drop…
Browse files Browse the repository at this point in the history
…down

Upgrade to searchable combobox from native dropdown #feature
  • Loading branch information
saleem-hadad authored Jul 15, 2022
2 parents e2fe90a + 5bfe3de commit 6ddf17a
Show file tree
Hide file tree
Showing 12 changed files with 10,487 additions and 10,449 deletions.
20,728 changes: 10,358 additions & 10,370 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@babel/plugin-transform-runtime": "^7.17.0",
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"@headlessui/react": "^1.4.2",
"@headlessui/react": "^1.6.6",
"@inertiajs/inertia": "^0.10.0",
"@inertiajs/inertia-react": "^0.7.0",
"@inertiajs/progress": "^0.2.6",
Expand Down
2 changes: 1 addition & 1 deletion public/css/app.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/js/app.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions public/mix-manifest.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"/js/app.js": "/js/app.js?id=a851dd183cd50e896983",
"/css/app.css": "/css/app.css?id=58adc592c995babafb87"
"/js/app.js": "/js/app.js?id=e96de6b2d1fad95be392",
"/css/app.css": "/css/app.css?id=98c4b8afbce17b6bb3ca"
}
4 changes: 2 additions & 2 deletions resources/js/Api/brands.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ export const createBrand = ({name, categoryId}) => {
.toPromise();
}

export const updateBrand = ({id, name, category}) => {
export const updateBrand = ({id, name, categoryId}) => {
return client
.mutation(gql`
mutation {
updateBrand(id: ${id} name: "${name}" category_id: ${category}) {
updateBrand(id: ${id} name: "${name}" category_id: ${categoryId}) {
id
name
category {
Expand Down
4 changes: 2 additions & 2 deletions resources/js/Api/transactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ export const createTransaction = ({amount, brandId, createdAt, note}) => {
.toPromise();
}

export const updateTransaction = ({id, amount, brand, createdAt, note}) => {
export const updateTransaction = ({id, amount, brandId, createdAt, note}) => {
return client
.mutation(gql`
mutation {
updateTransaction(id: ${id} amount: ${amount} brand_id: ${brand} created_at: """${createdAt}""" note: """${note}""") {
updateTransaction(id: ${id} amount: ${amount} brand_id: ${brandId} created_at: """${createdAt}""" note: """${note}""") {
id
amount
created_at
Expand Down
72 changes: 72 additions & 0 deletions resources/js/Components/Global/Combobox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { useState } from 'react'
import { CheckIcon, SelectorIcon } from '@heroicons/react/solid'
import { Combobox } from '@headlessui/react'

function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}

export default function ComboboxComponent({label, items, initialSelectedItem, onChange, displayInputValue, displayOptionValue}) {
const [query, setQuery] = useState('')
const [selectedItem, setSelectedItem] = useState(initialSelectedItem)

const filteredItems =
query === ''
? items
: items.filter((item) => {
return item.name.toLowerCase().includes(query.toLowerCase())
})

return (
<Combobox as="div" value={selectedItem} onChange={(item) => {
setSelectedItem(item)
onChange(item)
}}>
<Combobox.Label className="block text-sm font-medium text-gray-700">{label}</Combobox.Label>
<div className="relative mt-1">
<Combobox.Input
className="w-full rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 sm:text-sm"
onChange={(event) => setQuery(event.target.value)}
displayValue={(item) => displayInputValue ? displayInputValue(item) : item?.name}
/>
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none">
<SelectorIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</Combobox.Button>

{filteredItems.length > 0 && (
<Combobox.Options className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{filteredItems.map((item) => (
<Combobox.Option
key={item.id}
value={item}
className={({ active }) =>
classNames(
'relative cursor-default select-none py-2 pl-8 pr-4',
active ? 'bg-blue-500 text-white' : 'text-gray-900'
)
}
>
{({ active }) => (
<>
<span className={classNames('block truncate', item.id == selectedItem?.id && 'font-semibold')}>{displayOptionValue ? displayOptionValue(item) : item?.name}</span>

{item.id == selectedItem?.id && (
<span
className={classNames(
'absolute inset-y-0 left-0 flex items-center pl-1.5',
active ? 'text-white' : 'text-blue-500'
)}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
)}
</>
)}
</Combobox.Option>
))}
</Combobox.Options>
)}
</div>
</Combobox>
)
}
19 changes: 7 additions & 12 deletions resources/js/Pages/Brand/Create.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';

import Input from "@/Components/Global/Input";
import Label from "@/Components/Global/Label";
import Combobox from "@/Components/Global/Combobox";
import SidePanel from '@/Components/Global/SidePanel';
import { createBrand } from '../../Api';

Expand Down Expand Up @@ -49,19 +50,13 @@ export default function Edit({categories, showCreate, onClose, onCreate}) {
/>
</div>

<div className="col-span-6 sm:col-span-3 mt-4">
<Label forInput="category" value="Category" />

<select
id="category"
name="category"
value={categoryId}
onChange={(e) => setCategoryId(e.target.value)}
className="mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
>
<option value={0}>Select one</option>
{categories.map(item => <option value={item.id} key={item.id}>{item.name}</option>)}
</select>
<div className="col-span-6 sm:col-span-3 mt-4">
<Combobox
label="Category"
items={categories}
onChange={(item) => setCategoryId(item.id)}
/>
</div>

<div className="flex items-center justify-end mt-4">
Expand Down
33 changes: 14 additions & 19 deletions resources/js/Pages/Brand/Edit.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
import React, { useEffect, useState } from "react";

import { updateBrand } from "../../Api";
import Input from "@/Components/Global/Input";
import Label from "@/Components/Global/Label";
import Combobox from "@/Components/Global/Combobox";
import SidePanel from '@/Components/Global/SidePanel';
import { updateBrand } from "../../Api";

export default function Edit({categories, brand, onClose, onUpdate}) {
const [name, setName] = useState(0)
const [category, setCategory] = useState(0)
const [category, setCategory] = useState(null)

useEffect(() => {
if(! brand) return;

setName(brand.name)
if(brand.category) {
setCategory(brand.category.id)
setCategory(brand.category)
}
}, [brand])

const update = () => {
updateBrand({
id: brand.id,
name,
category
categoryId: category.id
})
.then(({data}) => {
onUpdate(data.updateBrand)
setCategory(0)
setCategory(null)
})
.catch(console.error);
}

let isReady = name != '' && category != 0;
let isReady = name != '' && category != null;

return (
<SidePanel toggleOpen={! brand ? false : true}
Expand All @@ -53,19 +54,13 @@ export default function Edit({categories, brand, onClose, onUpdate}) {
</div>

<div className="col-span-6 sm:col-span-3 mt-4">
<label htmlFor="category" className="block text-sm font-medium text-gray-700">
Category
</label>
<select
id="category"
name="category"
value={category}
onChange={(e) => setCategory(e.target.value)}
className="mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
>
<option value={0}>Select one</option>
{categories.map(item => <option value={item.id} key={item.id}>{item.name}</option>)}
</select>
<Combobox
label="Category"
items={categories}
initialSelectedItem={category}
onChange={(item) => setCategory(item)}
displayInputValue={(item) => item?.name ?? ''}
/>
</div>

<div className="flex items-center justify-end mt-4">
Expand Down
34 changes: 14 additions & 20 deletions resources/js/Pages/Transaction/Create.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,37 @@ import React, { useEffect, useState } from "react";
import Input from "@/Components/Global/Input";
import Label from "@/Components/Global/Label";
import { createTransaction } from "../../Api";
import Combobox from "@/Components/Global/Combobox";
import SidePanel from '@/Components/Global/SidePanel';

export default function Create({brands, showCreate, onClose, onCreate}) {
const [amount, setAmount] = useState(0);
const [brandId, setBrandId] = useState(0);
const [brand, setBrand] = useState(null);
const [createdAt, setCreatedAt] = useState('');
const [note, setNote] = useState('');
const [isReady, setIsReady] = useState(false);
const [loading, setLoading] = useState(false);

useEffect(() => {
setIsReady(amount != 0 && brandId != 0 && createdAt != '' ? true : false);
}, [amount, brandId, createdAt])
setIsReady(amount != 0 && brand != null && createdAt != '' ? true : false);
}, [amount, brand, createdAt])

const create = () => {
if(loading || ! isReady) { return; }
setLoading(true);

createTransaction({
amount,
brandId,
brandId: brand.id,
createdAt,
note
})
.then(({data}) => {
onCreate(data.createTransaction)
setBrandId(0)
setBrand(null)
setAmount(0)
setCreatedAt('')
setNote('')
setLoading(false);
})
.catch(console.error);
Expand Down Expand Up @@ -67,21 +69,13 @@ export default function Create({brands, showCreate, onClose, onCreate}) {
</div>

<div className="col-span-6 sm:col-span-3 mt-4">
<Label forInput="brand" value="Brand" />

<select
id="brand"
name="brand"
value={brandId}
onChange={(e) => setBrandId(e.target.value)}
className="mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
>
<option value={0}>Select one</option>
{brands.map(brand => <option value={brand.id} key={brand.id}>
{brand.name}
{brand.category ? " ("+brand.category.name+")" : ''}
</option>)}
</select>
<Combobox
label="Brand"
items={brands}
onChange={(item) => setBrand(item)}
displayInputValue={(item) => item ? `${item.name} (${item.category?.name ?? 'N/A'})` : ''}
displayOptionValue={(item) => item ? `${item.name} (${item.category?.name ?? 'N/A'})` : ''}
/>
</div>

<div className="mt-4">
Expand Down
32 changes: 13 additions & 19 deletions resources/js/Pages/Transaction/Edit.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import React, { useEffect, useState } from "react";

import { updateTransaction } from "../../Api";
import Input from "@/Components/Global/Input";
import Label from "@/Components/Global/Label";
import Combobox from "@/Components/Global/Combobox";
import SidePanel from '@/Components/Global/SidePanel';
import { updateTransaction } from "../../Api";

export default function Edit({brands, transaction, onClose, onUpdate}) {
const [amount, setAmount] = useState(0)
const [createdAt, setCreatedAt] = useState('')
const [brand, setBrand] = useState(0)
const [brand, setBrand] = useState(null)
const [note, setNote] = useState('')

useEffect(() => {
if(! transaction) return;

setAmount(transaction.amount)
setBrand(transaction.brand.id)
setBrand(transaction.brand)
setCreatedAt(transaction.created_at)
setNote(transaction.note ?? '')
}, [transaction])
Expand All @@ -24,7 +25,7 @@ export default function Edit({brands, transaction, onClose, onUpdate}) {
updateTransaction({
id: transaction.id,
amount,
brand,
brandId: brand.id,
createdAt,
note
})
Expand Down Expand Up @@ -66,21 +67,14 @@ export default function Edit({brands, transaction, onClose, onUpdate}) {
</div>

<div className="col-span-6 sm:col-span-3 mt-4">
<label htmlFor="brand" className="block text-sm font-medium text-gray-700">
Brand
</label>
<select
id="brand"
name="brand"
value={brand}
onChange={(e) => setBrand(e.target.value)}
className="mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
>
{brands.map(brand => <option value={brand.id} key={brand.id}>
{brand.name}
{brand.category ? " ("+brand.category.name+")" : ''}
</option>)}
</select>
<Combobox
label="Brand"
items={brands}
initialSelectedItem={brand}
onChange={(item) => setBrand(item)}
displayInputValue={(item) => item ? `${item.name} (${item.category?.name ?? 'N/A'})` : ''}
displayOptionValue={(item) => item ? `${item.name} (${item.category?.name ?? 'N/A'})` : ''}
/>
</div>

<div className="mt-4">
Expand Down

0 comments on commit 6ddf17a

Please sign in to comment.