update recipe viewer

This commit is contained in:
Kenta420 2024-02-27 13:43:26 +07:00
parent cf5b11b267
commit a12121fca6
19 changed files with 677 additions and 363 deletions

View file

@ -4,14 +4,17 @@ import { Separator } from '@/components/ui/separator'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { TooltipProvider } from '@/components/ui/tooltip'
import { cn } from '@/lib/utils'
import { type Recipe01, type Recipes } from '@/models/recipe/schema'
import { memo, useMemo, useState } from 'react'
import AndroidSwitcher from './android-switcher'
import { Search, CupSoda, Wheat, Dessert, Cherry, WineOff } from 'lucide-react'
import { type MaterialSetting, type Recipe01, type Recipes } from '@/models/recipe/schema'
import { memo, useEffect, useMemo, useState } from 'react'
import { Search, CupSoda, Wheat, Dessert, Cherry, WineOff, Server, Loader2 } from 'lucide-react'
import Nav from './nav'
import RecipeList from './recipe-list'
import { isBefore, isToday } from 'date-fns'
import { format, isBefore, isToday } from 'date-fns'
import RecipeDisplay from './recipe-display'
import MaterialList from './material-list'
import { Button } from '@/components/ui/button'
import { useMutation } from '@tanstack/react-query'
import taoAxios from '@/lib/taoAxios'
interface RecipeMenuProps {
recipes: Recipes
@ -20,13 +23,40 @@ interface RecipeMenuProps {
isDevBranch: boolean
}
const RecipeMenu: React.FC<RecipeMenuProps> = memo(({ recipes, recipe01, defaultSize, isDevBranch }) => {
recipe01 = useMemo(() => {
return recipe01.sort((a, b) => (a.LastChange && b.LastChange && isBefore(a.LastChange, b.LastChange) ? 1 : -1))
}, [recipe01])
const [recipeList, setRecipeList] = useState<Recipe01[]>(recipe01)
const sortedRecipe01 = useMemo(() => {
return recipeList.sort((a, b) => (a.LastChange && b.LastChange && isBefore(a.LastChange, b.LastChange) ? 1 : -1))
}, [recipeList])
const { isPending, isSuccess, isError, mutate } = useMutation({
mutationFn: async () => {
return taoAxios.post('/v2/recipes/', recipes, {
params: {
country_id: 'tha'
}
})
}
})
const [search, setSearch] = useState('')
useEffect(() => {
if (search) {
const recipesFiltered = recipe01.filter(
item =>
item.productCode.toLowerCase().includes(search.toLowerCase()) ||
(item.name && item.name.toLowerCase().includes(search.toLowerCase()))
)
setRecipeList(recipesFiltered)
} else {
setRecipeList(recipe01)
}
}, [search])
return (
<>
<ResizablePanel defaultSize={defaultSize} minSize={30}>
<ResizablePanel id="recipe-panel" defaultSize={defaultSize} minSize={30}>
<Tabs defaultValue="all">
<div className="flex items-center px-4 py-2">
<h1 className="text-xl font-bold">
@ -43,18 +73,37 @@ const RecipeMenu: React.FC<RecipeMenuProps> = memo(({ recipes, recipe01, default
</div>
<Separator />
<div className="bg-background/95 p-4 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="pb-3 flex justify-end items-end">
<Button className="bg-primary text-white" onClick={() => mutate()}>
{isPending ? (
<div className="flex items-center gap-2">
<Loader2 size={20} className="animate-spin" />
<span>Updating...</span>
</div>
) : isSuccess ? (
'Updated'
) : isError ? (
'Error'
) : (
<div className="flex items-center gap-2">
<Server size={20} />
Up Recipe to Server
</div>
)}
</Button>
</div>
<form>
<div className="relative">
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
<Input placeholder="Search" className="pl-8" />
<Input placeholder="Search" className="pl-8" value={search} onChange={e => setSearch(e.target.value)} />
</div>
</form>
</div>
<TabsContent value="all" className="m-0">
<RecipeList items={recipe01} />
<RecipeList items={sortedRecipe01} />
</TabsContent>
<TabsContent value="today" className="m-0">
<RecipeList items={recipe01.filter(item => item.LastChange && isToday(item.LastChange))} />
<RecipeList items={sortedRecipe01.filter(item => item.LastChange && isToday(item.LastChange))} />
</TabsContent>
</Tabs>
</ResizablePanel>
@ -66,13 +115,61 @@ const RecipeMenu: React.FC<RecipeMenuProps> = memo(({ recipes, recipe01, default
)
})
interface MaterialsProps {
recipes: Recipes
defaultSize?: number
isDevBranch: boolean
}
const Materials: React.FC<MaterialsProps> = memo(({ recipes, defaultSize, isDevBranch }) => {
const [materialSettingList, setMaterialSettingList] = useState<MaterialSetting[]>(recipes.MaterialSetting)
const sortedMaterialSettingList = useMemo(() => {
return materialSettingList.sort((a, b) => (a.id < b.id ? 1 : -1))
}, [materialSettingList])
const [search, setSearch] = useState('')
useEffect(() => {
if (search) {
const materialSettingsFiltered = recipes.MaterialSetting.filter(
item =>
item.materialName.toLowerCase().includes(search.toLowerCase()) ||
item.id.toString().includes(search.toLowerCase())
)
setMaterialSettingList(materialSettingsFiltered)
} else {
setMaterialSettingList(recipes.MaterialSetting)
}
}, [search])
return (
<>
<ResizablePanel id="material-panel" defaultSize={defaultSize} minSize={30}>
<div className="flex items-center px-4 py-2">
<h1 className="text-xl font-bold">
Recipe Version: {recipes.MachineSetting.configNumber} {isDevBranch ? '(Dev)' : ''}
</h1>
</div>
<Separator />
<div className="bg-background/95 p-4 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<form>
<div className="relative">
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
<Input placeholder="Search" className="pl-8" value={search} onChange={e => setSearch(e.target.value)} />
</div>
</form>
</div>
<MaterialList items={sortedMaterialSettingList} />
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={defaultSize}>
<RecipeDisplay recipes={recipes} />
</ResizablePanel>
</>
)
})
interface RecipeEditorProps {
androids: {
label: string
deviceName: string
serial: string
icon: React.ReactNode
}[]
isDevBranch: boolean
recipes: Recipes
defaultLayout: number[] | undefined
@ -81,7 +178,6 @@ interface RecipeEditorProps {
}
export const RecipeEditor: React.FC<RecipeEditorProps> = ({
androids,
recipes,
isDevBranch,
defaultLayout = [265, 440, 655],
@ -106,7 +202,7 @@ export const RecipeEditor: React.FC<RecipeEditorProps> = ({
onLayout={(sizes: number[]) => {
document.cookie = `react-resizable-panels:layout=${JSON.stringify(sizes)}`
}}
className="h-full max-h-[800px] items-stretch"
className="h-full max-h-[900px] items-stretch"
>
<ResizablePanel
defaultSize={defaultLayout[0]}
@ -125,7 +221,9 @@ export const RecipeEditor: React.FC<RecipeEditorProps> = ({
className={cn(isCollapsed && 'min-w-[50px] transition-all duration-300 ease-in-out')}
>
<div className={cn('flex h-[52px] items-center justify-center', isCollapsed ? 'h-[52px]' : 'px-2')}>
<AndroidSwitcher isCollapsed={isCollapsed} androids={androids} />
<span className={cn('text-muted-foreground text-xs ', isCollapsed && 'hidden')}>
TimeStamp: {format(recipes.Timestamp, 'dd-MM-yyyy HH:mm:ss')}
</span>
</div>
<Separator />
<Nav
@ -189,6 +287,8 @@ export const RecipeEditor: React.FC<RecipeEditorProps> = ({
defaultSize={defaultLayout[1]}
isDevBranch={isDevBranch}
/>
) : showListIndex === 2 ? (
<Materials recipes={recipes} defaultSize={defaultLayout[1]} isDevBranch={isDevBranch} />
) : null}
</ResizablePanelGroup>
</TooltipProvider>