feat(gear): connect CRUD operations and add category filter
This commit is contained in:
parent
bd810a931a
commit
d803d2bcfe
6 changed files with 426 additions and 21 deletions
|
|
@ -15,6 +15,8 @@ import {
|
|||
FileCheck,
|
||||
Download,
|
||||
Plus,
|
||||
Pencil,
|
||||
Trash2,
|
||||
} from 'lucide-react';
|
||||
import type { GearItem } from '@/types';
|
||||
import { getWarrantyStatus } from './gearUtils';
|
||||
|
|
@ -23,6 +25,8 @@ import { cn } from '@/lib/utils';
|
|||
export interface GearDetailModalProps {
|
||||
item: GearItem;
|
||||
onClose: () => void;
|
||||
onEdit?: (item: GearItem) => void;
|
||||
onDelete?: (item: GearItem) => void;
|
||||
onSellOnMarketplace?: (item: GearItem) => void;
|
||||
onLogMaintenance?: (item: GearItem) => void;
|
||||
onContactSupport?: (item: GearItem) => void;
|
||||
|
|
@ -33,6 +37,8 @@ export interface GearDetailModalProps {
|
|||
export function GearDetailModal({
|
||||
item,
|
||||
onClose,
|
||||
onEdit,
|
||||
onDelete,
|
||||
onSellOnMarketplace,
|
||||
onLogMaintenance,
|
||||
onContactSupport,
|
||||
|
|
@ -68,7 +74,28 @@ export function GearDetailModal({
|
|||
<h3 className="text-xl text-primary font-medium mb-4">
|
||||
{item.brand} {item.model}
|
||||
</h3>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{onEdit && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon={<Pencil className="w-4 h-4" />}
|
||||
onClick={() => onEdit(item)}
|
||||
>
|
||||
EDIT
|
||||
</Button>
|
||||
)}
|
||||
{onDelete && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
icon={<Trash2 className="w-4 h-4" />}
|
||||
onClick={() => onDelete(item)}
|
||||
className="text-destructive hover:text-destructive"
|
||||
>
|
||||
DELETE
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="glass"
|
||||
size="sm"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,265 @@
|
|||
/**
|
||||
* GearFormModal — Add/Edit gear item
|
||||
* v0.102: Connects to gearService.create/update
|
||||
*/
|
||||
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
import { X } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { gearService } from '@/services/gearService';
|
||||
import type { GearItem } from '@/types';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const gearFormSchema = z.object({
|
||||
name: z.string().min(1, 'Name is required').max(200),
|
||||
category: z.string().min(1, 'Category is required').max(100),
|
||||
brand: z.string().min(1, 'Brand is required').max(200),
|
||||
model: z.string().max(200).optional().or(z.literal('')),
|
||||
serialNumber: z.string().max(100).optional().or(z.literal('')),
|
||||
status: z.enum(['Active', 'Maintenance', 'Sold', 'Wishlist']),
|
||||
condition: z.enum(['Mint', 'Good', 'Fair', 'Poor']),
|
||||
purchaseDate: z.string().optional().or(z.literal('')),
|
||||
purchasePrice: z.coerce.number().min(0),
|
||||
currency: z.enum(['USD', 'EUR', 'GBP']),
|
||||
vendor: z.string().max(200).optional().or(z.literal('')),
|
||||
notes: z.string().max(2000).optional().or(z.literal('')),
|
||||
});
|
||||
|
||||
type GearFormData = z.infer<typeof gearFormSchema>;
|
||||
|
||||
export interface GearFormModalProps {
|
||||
item?: GearItem | null;
|
||||
onClose: () => void;
|
||||
onSuccess: () => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function GearFormModal({
|
||||
item,
|
||||
onClose,
|
||||
onSuccess,
|
||||
className,
|
||||
}: GearFormModalProps) {
|
||||
const isEdit = !!item;
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors, isSubmitting },
|
||||
} = useForm<GearFormData>({
|
||||
resolver: zodResolver(gearFormSchema),
|
||||
defaultValues: {
|
||||
name: item?.name ?? '',
|
||||
category: item?.category ?? 'Synth',
|
||||
brand: item?.brand ?? '',
|
||||
model: item?.model ?? '',
|
||||
serialNumber: item?.serialNumber ?? '',
|
||||
status: item?.status ?? 'Active',
|
||||
condition: item?.condition ?? 'Good',
|
||||
purchaseDate: item?.purchaseDate ?? '',
|
||||
purchasePrice: item?.purchasePrice ?? 0,
|
||||
currency: item?.currency ?? 'USD',
|
||||
vendor: item?.vendor ?? '',
|
||||
notes: item?.notes ?? '',
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = async (data: GearFormData) => {
|
||||
try {
|
||||
const payload: Partial<GearItem> = {
|
||||
name: data.name,
|
||||
category: data.category,
|
||||
brand: data.brand,
|
||||
model: data.model || undefined,
|
||||
serialNumber: data.serialNumber || undefined,
|
||||
status: data.status,
|
||||
condition: data.condition,
|
||||
purchaseDate: data.purchaseDate || undefined,
|
||||
purchasePrice: data.purchasePrice,
|
||||
currency: data.currency,
|
||||
vendor: data.vendor || undefined,
|
||||
notes: data.notes || undefined,
|
||||
};
|
||||
if (isEdit && item) {
|
||||
await gearService.update(item.id, payload);
|
||||
} else {
|
||||
await gearService.create(payload);
|
||||
}
|
||||
onSuccess();
|
||||
onClose();
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('fixed inset-0 z-50 flex items-center justify-center p-4', className)}>
|
||||
<div
|
||||
className="absolute inset-0 bg-background/90 backdrop-blur-sm"
|
||||
onClick={onClose}
|
||||
aria-hidden
|
||||
/>
|
||||
<div className="relative w-full max-w-2xl bg-card border border-border rounded-2xl shadow-2xl overflow-hidden max-h-[90vh] flex flex-col">
|
||||
<div className="p-6 border-b border-border bg-muted/30 flex justify-between items-center shrink-0">
|
||||
<h2 className="text-xl font-heading font-bold">
|
||||
{isEdit ? 'Edit Gear' : 'Add New Gear'}
|
||||
</h2>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="text-muted-foreground hover:text-foreground p-1"
|
||||
aria-label="Close"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
<form
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
className="flex-1 overflow-y-auto p-6 space-y-4"
|
||||
>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="md:col-span-2">
|
||||
<Label htmlFor="name" className="text-xs font-mono uppercase tracking-wider">
|
||||
Name *
|
||||
</Label>
|
||||
<Input
|
||||
id="name"
|
||||
{...register('name')}
|
||||
error={errors.name?.message}
|
||||
className="mt-1"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="category" className="text-xs font-mono uppercase tracking-wider">
|
||||
Category *
|
||||
</Label>
|
||||
<select
|
||||
id="category"
|
||||
{...register('category')}
|
||||
className="mt-1 flex h-11 w-full rounded-xl border border-border bg-background px-3 py-2 text-sm"
|
||||
>
|
||||
<option value="Synth">Synth</option>
|
||||
<option value="Interface">Interface</option>
|
||||
<option value="Microphone">Microphone</option>
|
||||
<option value="Monitor">Monitor</option>
|
||||
<option value="Controller">Controller</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="brand" className="text-xs font-mono uppercase tracking-wider">
|
||||
Brand *
|
||||
</Label>
|
||||
<Input id="brand" {...register('brand')} error={errors.brand?.message} className="mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="model" className="text-xs font-mono uppercase tracking-wider">
|
||||
Model
|
||||
</Label>
|
||||
<Input id="model" {...register('model')} className="mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="serialNumber" className="text-xs font-mono uppercase tracking-wider">
|
||||
Serial Number
|
||||
</Label>
|
||||
<Input id="serialNumber" {...register('serialNumber')} className="mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="status" className="text-xs font-mono uppercase tracking-wider">
|
||||
Status
|
||||
</Label>
|
||||
<select
|
||||
id="status"
|
||||
{...register('status')}
|
||||
className="mt-1 flex h-11 w-full rounded-xl border border-border bg-background px-3 py-2 text-sm"
|
||||
>
|
||||
<option value="Active">Active</option>
|
||||
<option value="Maintenance">Maintenance</option>
|
||||
<option value="Sold">Sold</option>
|
||||
<option value="Wishlist">Wishlist</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="condition" className="text-xs font-mono uppercase tracking-wider">
|
||||
Condition
|
||||
</Label>
|
||||
<select
|
||||
id="condition"
|
||||
{...register('condition')}
|
||||
className="mt-1 flex h-11 w-full rounded-xl border border-border bg-background px-3 py-2 text-sm"
|
||||
>
|
||||
<option value="Mint">Mint</option>
|
||||
<option value="Good">Good</option>
|
||||
<option value="Fair">Fair</option>
|
||||
<option value="Poor">Poor</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="purchaseDate" className="text-xs font-mono uppercase tracking-wider">
|
||||
Purchase Date
|
||||
</Label>
|
||||
<Input
|
||||
id="purchaseDate"
|
||||
type="date"
|
||||
{...register('purchaseDate')}
|
||||
className="mt-1"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="purchasePrice" className="text-xs font-mono uppercase tracking-wider">
|
||||
Purchase Price
|
||||
</Label>
|
||||
<Input
|
||||
id="purchasePrice"
|
||||
type="number"
|
||||
step="0.01"
|
||||
{...register('purchasePrice')}
|
||||
error={errors.purchasePrice?.message}
|
||||
className="mt-1"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="currency" className="text-xs font-mono uppercase tracking-wider">
|
||||
Currency
|
||||
</Label>
|
||||
<select
|
||||
id="currency"
|
||||
{...register('currency')}
|
||||
className="mt-1 flex h-11 w-full rounded-xl border border-border bg-background px-3 py-2 text-sm"
|
||||
>
|
||||
<option value="USD">USD</option>
|
||||
<option value="EUR">EUR</option>
|
||||
<option value="GBP">GBP</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<Label htmlFor="vendor" className="text-xs font-mono uppercase tracking-wider">
|
||||
Vendor
|
||||
</Label>
|
||||
<Input id="vendor" {...register('vendor')} className="mt-1" />
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<Label htmlFor="notes" className="text-xs font-mono uppercase tracking-wider">
|
||||
Notes
|
||||
</Label>
|
||||
<Textarea id="notes" {...register('notes')} rows={3} className="mt-1" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 pt-4 border-t border-border">
|
||||
<Button type="button" variant="ghost" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" disabled={isSubmitting}>
|
||||
{isSubmitting ? 'Saving...' : isEdit ? 'Save' : 'Add Gear'}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ export { GearInventoryGrid } from './GearInventoryGrid';
|
|||
export { GearInventoryGridSkeleton } from './GearInventoryGridSkeleton';
|
||||
export { GearCard } from './GearCard';
|
||||
export { GearDetailModal } from './GearDetailModal';
|
||||
export { GearFormModal } from './GearFormModal';
|
||||
export { getWarrantyStatus } from './gearUtils';
|
||||
export type { GearViewHeaderProps } from './GearViewHeader';
|
||||
export type { GearFiltersProps } from './GearFilters';
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import toast from '@/utils/toast';
|
||||
import {
|
||||
GearViewHeader,
|
||||
GearFilters,
|
||||
GearInventoryGrid,
|
||||
GearDetailModal,
|
||||
GearFormModal,
|
||||
} from '../../components/gear';
|
||||
import { ConfirmationDialog } from '@/components/ui/confirmation-dialog';
|
||||
import type { GearItem } from '@/types';
|
||||
import { gearService } from '@/services/gearService';
|
||||
import { useGearView } from './useGearView';
|
||||
import { GearViewSkeleton } from './GearViewSkeleton';
|
||||
import { GearViewToolbar } from './GearViewToolbar';
|
||||
|
|
@ -25,6 +28,14 @@ export const GearView: React.FC<GearViewProps> = ({
|
|||
isLoading: isLoadingProp,
|
||||
error: errorProp,
|
||||
}) => {
|
||||
const [formModalItem, setFormModalItem] = useState<GearItem | null | 'add'>(
|
||||
null,
|
||||
);
|
||||
const [deleteConfirmItem, setDeleteConfirmItem] = useState<GearItem | null>(
|
||||
null,
|
||||
);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
|
||||
const {
|
||||
filter,
|
||||
setFilter,
|
||||
|
|
@ -37,6 +48,7 @@ export const GearView: React.FC<GearViewProps> = ({
|
|||
filteredInventory,
|
||||
isLoading,
|
||||
error,
|
||||
refetch,
|
||||
} = useGearView({
|
||||
itemsOverride,
|
||||
isLoading: isLoadingProp,
|
||||
|
|
@ -48,6 +60,36 @@ export const GearView: React.FC<GearViewProps> = ({
|
|||
setSelectedItem(null);
|
||||
};
|
||||
|
||||
const handleOpenAddForm = () => setFormModalItem('add');
|
||||
const handleOpenEditForm = (item: GearItem) => {
|
||||
setSelectedItem(null);
|
||||
setFormModalItem(item);
|
||||
};
|
||||
const handleCloseFormModal = () => setFormModalItem(null);
|
||||
const handleFormSuccess = () => {
|
||||
void refetch();
|
||||
};
|
||||
|
||||
const handleDeleteClick = (item: GearItem) => {
|
||||
setSelectedItem(null);
|
||||
setDeleteConfirmItem(item);
|
||||
};
|
||||
const handleDeleteConfirm = async () => {
|
||||
if (!deleteConfirmItem) return;
|
||||
setIsDeleting(true);
|
||||
try {
|
||||
await gearService.delete(deleteConfirmItem.id);
|
||||
toast.success('Gear item deleted');
|
||||
setSelectedItem(null);
|
||||
setDeleteConfirmItem(null);
|
||||
void refetch();
|
||||
} catch (err) {
|
||||
toast.error(err instanceof Error ? err.message : 'Failed to delete');
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return <GearViewSkeleton />;
|
||||
}
|
||||
|
|
@ -56,7 +98,7 @@ export const GearView: React.FC<GearViewProps> = ({
|
|||
<div className="space-y-8 animate-fadeIn relative max-w-layout-content mx-auto px-4 md:px-6">
|
||||
<GearViewHeader
|
||||
onExport={() => toast('Exporting Inventory CSV...')}
|
||||
onRegister={() => toast('Opens Registration Form')}
|
||||
onRegister={handleOpenAddForm}
|
||||
error={error}
|
||||
/>
|
||||
{error ? (
|
||||
|
|
@ -65,7 +107,7 @@ export const GearView: React.FC<GearViewProps> = ({
|
|||
viewMode={viewMode}
|
||||
error={error}
|
||||
onItemSelect={setSelectedItem}
|
||||
onAddNew={() => toast('Opens Registration Form')}
|
||||
onAddNew={handleOpenAddForm}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
|
|
@ -80,18 +122,41 @@ export const GearView: React.FC<GearViewProps> = ({
|
|||
items={filteredInventory}
|
||||
viewMode={viewMode}
|
||||
onItemSelect={setSelectedItem}
|
||||
onAddNew={() => toast('Opens Registration Form')}
|
||||
onAddNew={handleOpenAddForm}
|
||||
/>
|
||||
{selectedItem && (
|
||||
<GearDetailModal
|
||||
item={selectedItem}
|
||||
onClose={() => setSelectedItem(null)}
|
||||
onEdit={handleOpenEditForm}
|
||||
onDelete={handleDeleteClick}
|
||||
onSellOnMarketplace={handleListOnMarketplace}
|
||||
onLogMaintenance={() => toast('Maintenance Log Updated')}
|
||||
onContactSupport={(item) => toast(`Contacting ${item.supportContact}`)}
|
||||
onUploadDocument={() => toast('Upload document')}
|
||||
/>
|
||||
)}
|
||||
{formModalItem !== null && (
|
||||
<GearFormModal
|
||||
item={formModalItem === 'add' ? undefined : formModalItem}
|
||||
onClose={handleCloseFormModal}
|
||||
onSuccess={handleFormSuccess}
|
||||
/>
|
||||
)}
|
||||
<ConfirmationDialog
|
||||
open={!!deleteConfirmItem}
|
||||
onClose={() => setDeleteConfirmItem(null)}
|
||||
onConfirm={handleDeleteConfirm}
|
||||
title="Delete gear item"
|
||||
description={
|
||||
deleteConfirmItem
|
||||
? `Are you sure you want to delete "${deleteConfirmItem.name}"? This cannot be undone.`
|
||||
: ''
|
||||
}
|
||||
confirmLabel="Delete"
|
||||
variant="destructive"
|
||||
isLoading={isDeleting}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useMemo, useEffect } from 'react';
|
||||
import { useState, useMemo, useEffect, useCallback } from 'react';
|
||||
import type { GearItem } from '@/types';
|
||||
import {
|
||||
type GearViewMode,
|
||||
|
|
@ -27,6 +27,7 @@ export interface UseGearViewReturn {
|
|||
filteredInventory: GearItem[];
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
refetch: () => Promise<void>;
|
||||
}
|
||||
|
||||
export function useGearView(props: UseGearViewProps = {}): UseGearViewReturn {
|
||||
|
|
@ -39,24 +40,25 @@ export function useGearView(props: UseGearViewProps = {}): UseGearViewReturn {
|
|||
const [fetchLoading, setFetchLoading] = useState(false);
|
||||
const [fetchError, setFetchError] = useState<string | null>(null);
|
||||
|
||||
const fetchItems = useCallback(async () => {
|
||||
setFetchLoading(true);
|
||||
setFetchError(null);
|
||||
try {
|
||||
const items = await gearService.list();
|
||||
setFetchedItems(items);
|
||||
} catch (err) {
|
||||
setFetchError(err instanceof Error ? err.message : 'Failed to load gear');
|
||||
setFetchedItems([]);
|
||||
} finally {
|
||||
setFetchLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// When itemsOverride is undefined, fetch from API
|
||||
useEffect(() => {
|
||||
if (itemsOverride !== undefined) return;
|
||||
setFetchLoading(true);
|
||||
setFetchError(null);
|
||||
gearService
|
||||
.list()
|
||||
.then((items) => {
|
||||
setFetchedItems(items);
|
||||
})
|
||||
.catch((err) => {
|
||||
setFetchError(err instanceof Error ? err.message : 'Failed to load gear');
|
||||
setFetchedItems([]);
|
||||
})
|
||||
.finally(() => {
|
||||
setFetchLoading(false);
|
||||
});
|
||||
}, [itemsOverride]);
|
||||
void fetchItems();
|
||||
}, [itemsOverride, fetchItems]);
|
||||
|
||||
const sourceItems =
|
||||
itemsOverride !== undefined ? (itemsOverride ?? []) : fetchedItems;
|
||||
|
|
@ -87,5 +89,6 @@ export function useGearView(props: UseGearViewProps = {}): UseGearViewReturn {
|
|||
filteredInventory,
|
||||
isLoading,
|
||||
error,
|
||||
refetch: fetchItems,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,6 +252,50 @@ export const handlersMisc = [
|
|||
});
|
||||
}),
|
||||
|
||||
http.post('*/api/v1/inventory/gear', async ({ request }) => {
|
||||
const body = (await request.json()) as Record<string, unknown>;
|
||||
const item = {
|
||||
id: `gear-${Date.now()}`,
|
||||
name: body.name ?? 'New Gear',
|
||||
category: body.category ?? 'Synth',
|
||||
brand: body.brand ?? '',
|
||||
model: body.model ?? '',
|
||||
serialNumber: body.serialNumber ?? '',
|
||||
purchaseDate: body.purchaseDate ?? '',
|
||||
purchasePrice: body.purchasePrice ?? 0,
|
||||
currency: body.currency ?? 'USD',
|
||||
status: body.status ?? 'Active',
|
||||
condition: body.condition ?? 'Good',
|
||||
vendor: body.vendor ?? '',
|
||||
image: 'https://picsum.photos/id/100/400/400',
|
||||
};
|
||||
return HttpResponse.json({ success: true, data: { item } }, { status: 201 });
|
||||
}),
|
||||
|
||||
http.put('*/api/v1/inventory/gear/:id', async ({ request, params }) => {
|
||||
const body = (await request.json()) as Record<string, unknown>;
|
||||
const item = {
|
||||
id: params.id,
|
||||
name: body.name ?? 'Updated',
|
||||
category: body.category ?? 'Synth',
|
||||
brand: body.brand ?? '',
|
||||
model: body.model ?? '',
|
||||
serialNumber: body.serialNumber ?? '',
|
||||
purchaseDate: body.purchaseDate ?? '',
|
||||
purchasePrice: body.purchasePrice ?? 0,
|
||||
currency: body.currency ?? 'USD',
|
||||
status: body.status ?? 'Active',
|
||||
condition: body.condition ?? 'Good',
|
||||
vendor: body.vendor ?? '',
|
||||
image: 'https://picsum.photos/id/100/400/400',
|
||||
};
|
||||
return HttpResponse.json({ success: true, data: { item } });
|
||||
}),
|
||||
|
||||
http.delete('*/api/v1/inventory/gear/:id', () => {
|
||||
return HttpResponse.json({ success: true, message: 'gear item deleted' });
|
||||
}),
|
||||
|
||||
http.get('*/api/v1/live/streams', ({ request }) => {
|
||||
const url = new URL(request.url);
|
||||
const isLive = url.searchParams.get('is_live');
|
||||
|
|
|
|||
Loading…
Reference in a new issue