Skip to content

Commit

Permalink
Combobox dropdown
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusab committed May 13, 2024
1 parent 093acde commit 2445e8c
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 112 deletions.
Expand Up @@ -3,7 +3,6 @@ import {
updateCategorySchema,
} from "@/actions/schema";
import { updateCategoryAction } from "@/actions/update-category-action";
import { VatAssistant } from "@/components/vat-assistant";
import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "@midday/ui/button";
import {
Expand Down
189 changes: 81 additions & 108 deletions apps/dashboard/src/components/select-category.tsx
@@ -1,9 +1,11 @@
import { createCategoriesAction } from "@/actions/create-categories-action";
import { searchAction } from "@/actions/search-action";
import { getColorFromName } from "@/utils/categories";
import { cn } from "@midday/ui/cn";
import { Combobox } from "@midday/ui/combobox";
import { useDebounce } from "@uidotdev/usehooks";
import { createClient } from "@midday/supabase/client";
import {
getCategoriesQuery,
getCurrentUserTeamQuery,
} from "@midday/supabase/queries";
import { ComboboxDropdown } from "@midday/ui/combobox-dropdown";
import { useAction } from "next-safe-action/hooks";
import { useEffect, useState } from "react";
import { CategoryColor } from "./category";
Expand All @@ -17,131 +19,102 @@ type Selected = {

type Props = {
selected?: Selected;
placeholder: string;
onChange: (selected: Selected) => void;
};

export function SelectCategory({ selected, placeholder, onChange }: Props) {
const [query, setQuery] = useState("");
function transformCategory(category) {
return {
id: category.id,
label: category.name,
color: category.color,
slug: category.slug,
};
}

export function SelectCategory({ selected, onChange }: Props) {
const [data, setData] = useState([]);
const [isLoading, setLoading] = useState(false);
const supabase = createClient();

useEffect(() => {
async function fetchData() {
const { data: userData } = await getCurrentUserTeamQuery(supabase);
if (userData?.team_id) {
const response = await getCategoriesQuery(supabase, {
teamId: userData.team_id,
limit: 1000,
});

if (response.data) {
setData(response.data.map(transformCategory));
}
}
}

if (!data.length) {
fetchData();
}
}, [data]);

const createCategories = useAction(createCategoriesAction, {
onSuccess: (data) => {
const category = data.at(0);

if (category) {
setData((prev) => [category, ...prev]);
setData((prev) => [transformCategory(category), ...prev]);
onChange(category);
}
},
});

const debouncedSearchTerm = useDebounce(query, 50);
const selectedValue = selected ? transformCategory(selected) : undefined;

const search = useAction(searchAction, {
onSuccess: (response) => {
setData(response);
setLoading(false);
},
onError: () => setLoading(false),
});

useEffect(() => {
if (debouncedSearchTerm) {
search.execute({
query: debouncedSearchTerm,
type: "categories",
limit: 10,
});
}
}, [debouncedSearchTerm]);

const options = data?.map((option) => ({
id: option.id,
name: option.name,
data: option,
component: () => {
return (
return (
<ComboboxDropdown
disabled={createCategories.status === "executing"}
placeholder="Select category"
searchPlaceholder="Search category"
items={data}
selectedItem={selectedValue}
onSelect={(item) => {
onChange({
id: item.id,
name: item.label,
color: item.color,
slug: item.slug,
});
}}
onCreate={(value) => {
createCategories.execute({
categories: [
{
name: value,
color: getColorFromName(value),
},
],
});
}}
renderSelectedItem={(selectedItem) => (
<div className="flex items-center space-x-2">
<div
className="rounded-[2px] size-3"
style={{ backgroundColor: option.color }}
/>
<span>{option.name}</span>
<CategoryColor color={selectedItem.color} />
<span>{selectedItem.label}</span>
</div>
);
},
}));

const onSelect = (option) => {
onChange({
id: option.id,
name: option.name,
color: option.data.color,
slug: option.data.slug,
});
};

const onCreate = (value: string) => {
createCategories.execute({
categories: [
{
name: value,
color: getColorFromName(value),
},
],
});
};

const selectedValue = selected
? {
id: selected.id,
name: selected.name,
}
: undefined;

return (
<div className="relative">
{selected && (
<CategoryColor
className="absolute top-[12px] left-2"
color={selected.color}
/>
)}

<Combobox
key={selected?.id}
showIcon={false}
className={cn(
"border border-border rounded-md p-2 h-9",
selectedValue && "pl-7"
)}
placeholder={placeholder}
value={selectedValue}
CreateComponent={({ value }) => (
renderOnCreate={(value) => {
return (
<div className="flex items-center space-x-2">
<div
className="rounded-[2px] size-3 transition-colors"
style={{ backgroundColor: getColorFromName(value) }}
/>
<CategoryColor color={getColorFromName(value)} />
<span>{`Create "${value}"`}</span>
</div>
)}
onCreate={onCreate}
onValueChange={(q) => {
if (q) {
setLoading(true);
setQuery(q);
} else {
setLoading(false);
}
}}
onSelect={onSelect}
options={options}
isLoading={isLoading}
classNameList="mt-2"
/>
</div>
);
}}
renderListItem={({ item }) => {
return (
<div className="flex items-center space-x-2">
<CategoryColor color={item.color} />
<span>{item.label}</span>
</div>
);
}}
/>
);
}
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/sheets/transaction-sheet.tsx
@@ -1,4 +1,4 @@
import { UpdateTransactionValues } from "@/actions/schema";
import type { UpdateTransactionValues } from "@/actions/schema";
import { useMediaQuery } from "@/hooks/use-media-query";
import { Drawer, DrawerContent } from "@midday/ui/drawer";
import { Sheet, SheetContent } from "@midday/ui/sheet";
Expand Down
2 changes: 0 additions & 2 deletions apps/dashboard/src/components/transaction-details.tsx
Expand Up @@ -221,8 +221,6 @@ export function TransactionDetails({
</Label>

<SelectCategory
placeholder="Category"
name={data?.name}
id={transactionId}
selected={data?.category}
onChange={handleOnChangeCategory}
Expand Down
1 change: 1 addition & 0 deletions packages/ui/package.json
Expand Up @@ -35,6 +35,7 @@
"./checkbox": "./src/components/checkbox.tsx",
"./collapsible": "./src/components/collapsible.tsx",
"./combobox": "./src/components/combobox.tsx",
"./combobox-dropdown": "./src/components/combobox-dropdown.tsx",
"./command": "./src/components/command.tsx",
"./context-menu": "./src/components/context-menu.tsx",
"./date-range-picker": "./src/components/date-range-picker.tsx",
Expand Down

0 comments on commit 2445e8c

Please sign in to comment.