From fb49fb596a56955ca70eac9da47d2826299469e1 Mon Sep 17 00:00:00 2001
From: Mert Demir <mertd@users.noreply.github.com>
Date: Mon, 5 Feb 2024 02:23:11 +0900
Subject: Automatic category from expense title (#80)

* environment variable

* random category draft

* get category from ai

* input limit and documentation

* use watch

* use field.name

* prettier

* presigned upload, readme warning, category to string util

* prettier

* check whether feature is enabled

* use process.env

* improved prompt to return id only

* remove console.debug

* show loader

* share class name

* prettier

* use template literals

* rename format util

* prettier
---
 src/components/category-selector.tsx | 38 ++++++++++++++++++++++++++++++------
 1 file changed, 32 insertions(+), 6 deletions(-)

(limited to 'src/components/category-selector.tsx')

diff --git a/src/components/category-selector.tsx b/src/components/category-selector.tsx
index 8e6c5a6..05c3be0 100644
--- a/src/components/category-selector.tsx
+++ b/src/components/category-selector.tsx
@@ -1,4 +1,4 @@
-import { ChevronDown } from 'lucide-react'
+import { ChevronDown, Loader2 } from 'lucide-react'
 
 import { CategoryIcon } from '@/app/groups/[groupId]/expenses/category-icon'
 import { Button, ButtonProps } from '@/components/ui/button'
@@ -17,23 +17,32 @@ import {
 } from '@/components/ui/popover'
 import { useMediaQuery } from '@/lib/hooks'
 import { Category } from '@prisma/client'
-import { forwardRef, useState } from 'react'
+import { forwardRef, useEffect, useState } from 'react'
 
 type Props = {
   categories: Category[]
   onValueChange: (categoryId: Category['id']) => void
+  /** Category ID to be selected by default. Overwriting this value will update current selection, too. */
   defaultValue: Category['id']
+  isLoading: boolean
 }
 
 export function CategorySelector({
   categories,
   onValueChange,
   defaultValue,
+  isLoading,
 }: Props) {
   const [open, setOpen] = useState(false)
   const [value, setValue] = useState<number>(defaultValue)
   const isDesktop = useMediaQuery('(min-width: 768px)')
 
+  // allow overwriting currently selected category from outside
+  useEffect(() => {
+    setValue(defaultValue)
+    onValueChange(defaultValue)
+  }, [defaultValue])
+
   const selectedCategory =
     categories.find((category) => category.id === value) ?? categories[0]
 
@@ -41,7 +50,11 @@ export function CategorySelector({
     return (
       <Popover open={open} onOpenChange={setOpen}>
         <PopoverTrigger asChild>
-          <CategoryButton category={selectedCategory} open={open} />
+          <CategoryButton
+            category={selectedCategory}
+            open={open}
+            isLoading={isLoading}
+          />
         </PopoverTrigger>
         <PopoverContent className="p-0" align="start">
           <CategoryCommand
@@ -60,7 +73,11 @@ export function CategorySelector({
   return (
     <Drawer open={open} onOpenChange={setOpen}>
       <DrawerTrigger asChild>
-        <CategoryButton category={selectedCategory} open={open} />
+        <CategoryButton
+          category={selectedCategory}
+          open={open}
+          isLoading={isLoading}
+        />
       </DrawerTrigger>
       <DrawerContent className="p-0">
         <CategoryCommand
@@ -122,9 +139,14 @@ function CategoryCommand({
 type CategoryButtonProps = {
   category: Category
   open: boolean
+  isLoading: boolean
 }
 const CategoryButton = forwardRef<HTMLButtonElement, CategoryButtonProps>(
-  ({ category, open, ...props }: ButtonProps & CategoryButtonProps, ref) => {
+  (
+    { category, open, isLoading, ...props }: ButtonProps & CategoryButtonProps,
+    ref,
+  ) => {
+    const iconClassName = 'ml-2 h-4 w-4 shrink-0 opacity-50'
     return (
       <Button
         variant="outline"
@@ -135,7 +157,11 @@ const CategoryButton = forwardRef<HTMLButtonElement, CategoryButtonProps>(
         {...props}
       >
         <CategoryLabel category={category} />
-        <ChevronDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
+        {isLoading ? (
+          <Loader2 className={`animate-spin ${iconClassName}`} />
+        ) : (
+          <ChevronDown className={iconClassName} />
+        )}
       </Button>
     )
   },
-- 
cgit v1.3

