Fixes #21

Merged
DeepnaPooja merged 61 commits from fixes into dev 2026-09-10 16:00:04 +05:30
3 changed files with 105 additions and 3 deletions
Showing only changes of commit aee3e840a0 - Show all commits
@@ -13,6 +13,7 @@ import { categories as categoriesApi, getApiError } from '@/lib/api/index'
import type { CategoryResponse } from '@/types/api'
import { CategorySheet } from './category-sheet'
import { CategoryViewSheet } from './category-view-sheet'
export function CategoriesClient() {
const [data, setData] = useState<CategoryResponse[]>([])
@@ -21,6 +22,8 @@ export function CategoriesClient() {
const [sheetOpen, setSheetOpen] = useState(false)
const [editing, setEditing] = useState<CategoryResponse | null>(null)
const [viewOpen, setViewOpen] = useState(false)
const [viewing, setViewing] = useState<CategoryResponse | null>(null)
const fetchData = useCallback(async () => {
setLoading(true)
@@ -49,6 +52,21 @@ export function CategoriesClient() {
setSheetOpen(true)
}
function openView(category: CategoryResponse) {
setViewing(category)
setViewOpen(true)
}
async function handleDelete(category: CategoryResponse) {
try {
await categoriesApi.remove(category.id)
toast.add({ title: 'Category deactivated', type: 'success' })
fetchData()
} catch (err) {
toast.add({ title: 'Failed to deactivate category', description: getApiError(err), type: 'error' })
}
}
const tableConfig = useMemo<TableConfig<CategoryResponse>>(() => ({
rowKey: 'id',
serverSide: false,
@@ -99,11 +117,22 @@ export function CategoriesClient() {
],
rowActions: {
actions: () => [
{
action: 'view',
label: 'View category',
onClick: openView,
},
{
action: 'edit',
label: 'Edit category',
onClick: openEdit,
},
{
action: 'cancel',
label: 'Delete category',
onClick: handleDelete,
visible: (target) => target.isActive,
},
],
},
}), [openEdit])
@@ -135,6 +164,12 @@ export function CategoriesClient() {
category={editing}
onSuccess={() => fetchData()}
/>
<CategoryViewSheet
open={viewOpen}
onClose={() => setViewOpen(false)}
category={viewing}
/>
</>
)
}
@@ -0,0 +1,67 @@
'use client'
import { Badge } from '@/components/ui/badge'
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { Separator } from '@/components/ui/separator'
import type { CategoryResponse } from '@/types/api'
const fmtDate = (v: string) =>
new Date(v).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })
interface CategoryViewSheetProps {
open: boolean
onClose: () => void
category: CategoryResponse | null
}
function Row({ label, value }: { label: string; value: React.ReactNode }) {
return (
<div className="flex items-center justify-between py-2 text-sm border-b last:border-0">
<span className="text-muted-foreground">{label}</span>
<span className="font-medium text-right">{value}</span>
</div>
)
}
export function CategoryViewSheet({ open, onClose, category }: CategoryViewSheetProps) {
if (!category) return null
return (
<Dialog open={open} onOpenChange={(v) => !v && onClose()}>
<DialogContent className="overflow-y-auto sm:max-w-lg">
<DialogHeader>
<DialogTitle>Category Details</DialogTitle>
</DialogHeader>
<div className="flex flex-col">
<Row label="Name" value={category.name} />
<Row label="Status" value={<Badge variant={category.isActive ? 'default' : 'secondary'}>{category.isActive ? 'Active' : 'Inactive'}</Badge>} />
<Row label="Created At" value={fmtDate(category.createdAt)} />
</div>
<Separator />
<div>
<p className="text-sm font-medium mb-2">Sub-categories ({category.subCategories.length})</p>
{category.subCategories.length === 0 ? (
<p className="text-sm text-muted-foreground">No sub-categories yet.</p>
) : (
<div className="flex flex-col gap-1">
{category.subCategories.map((sub) => (
<div key={sub.id} className="flex items-center justify-between rounded-md border px-3 py-2 text-sm">
<span>{sub.name}</span>
<Badge variant={sub.isActive ? 'default' : 'secondary'}>{sub.isActive ? 'Active' : 'Inactive'}</Badge>
</div>
))}
</div>
)}
</div>
</DialogContent>
</Dialog>
)
}
@@ -23,7 +23,7 @@ import {
SelectValue,
} from '@/components/ui/select'
import { toast } from '@/components/ui/toast'
import { items as itemsApi, categories as categoriesApi, brands as brandsApi, uoms as uomsApi, getApiError, getPaginatedItems } from '@/lib/api/index'
import { items as itemsApi, categories as categoriesApi, brands as brandsApi, uoms as uomsApi, getApiError } from '@/lib/api/index'
import type { ItemSummaryResponse, CategoryResponse, BrandResponse, UomResponse } from '@/types/api'
const schema = z.object({
@@ -69,11 +69,11 @@ export function ItemSheet({ open, onClose, item, onSuccess }: ItemSheetProps) {
if (open) {
Promise.all([
categoriesApi.list(),
brandsApi.list({ page: 1, pageSize: 200 }),
brandsApi.list(),
uomsApi.list(),
]).then(([cats, brandsRes, uomsRes]) => {
setCategories(cats)
setBrandsList(getPaginatedItems(brandsRes))
setBrandsList(brandsRes)
setUomsList(uomsRes)
}).catch((err) => {
toast.add({ title: 'Failed to load reference data', description: getApiError(err), type: 'error' })