wip: in-progress feature work (manual entry, collection view)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import { toast } from "sonner";
|
||||
import { useCategories } from "../hooks/useCategories";
|
||||
import { useCreateItem } from "../hooks/useItems";
|
||||
import { useUIStore } from "../stores/uiStore";
|
||||
import { CategoryPicker } from "./CategoryPicker";
|
||||
|
||||
export function AddToCollectionModal() {
|
||||
const { open, globalItemId, globalItemName } = useUIStore(
|
||||
@@ -95,24 +96,13 @@ export function AddToCollectionModal() {
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="collection-category"
|
||||
className="block text-sm font-medium text-gray-700 mb-1"
|
||||
>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Category
|
||||
</label>
|
||||
<select
|
||||
id="collection-category"
|
||||
value={categoryId ?? ""}
|
||||
onChange={(e) => setCategoryId(Number(e.target.value))}
|
||||
className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-gray-400 focus:border-transparent bg-white"
|
||||
>
|
||||
{categories?.map((cat) => (
|
||||
<option key={cat.id} value={cat.id}>
|
||||
{cat.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<CategoryPicker
|
||||
value={categoryId ?? 0}
|
||||
onChange={(id) => setCategoryId(id)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -230,6 +230,7 @@ export function CollectionView() {
|
||||
imageFilename={item.imageFilename}
|
||||
imageUrl={item.imageUrl}
|
||||
productUrl={item.productUrl}
|
||||
brand={item.brand}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -264,6 +265,7 @@ export function CollectionView() {
|
||||
categoryIcon={categoryIcon}
|
||||
imageFilename={item.imageFilename}
|
||||
productUrl={item.productUrl}
|
||||
brand={item.brand}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -34,6 +34,8 @@ interface ItemWithCategory {
|
||||
notes: string | null;
|
||||
productUrl: string | null;
|
||||
imageFilename: string | null;
|
||||
globalItemId: number | null;
|
||||
brand: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
categoryName: string;
|
||||
|
||||
@@ -223,6 +223,7 @@ export const iconGroups: IconGroup[] = [
|
||||
// --- LucideIcon render component ---
|
||||
|
||||
function toPascalCase(str: string): string {
|
||||
if (!str) return "Package";
|
||||
return str
|
||||
.split("-")
|
||||
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
||||
|
||||
@@ -35,6 +35,9 @@ export async function getAllItems(db: Db, userId: number) {
|
||||
)`.as("image_filename"),
|
||||
imageSourceUrl: items.imageSourceUrl,
|
||||
globalItemId: items.globalItemId,
|
||||
brand: sql<
|
||||
string | null
|
||||
>`COALESCE(${globalItems.brand}, ${items.brand})`.as("brand"),
|
||||
createdAt: items.createdAt,
|
||||
updatedAt: items.updatedAt,
|
||||
categoryName: categories.name,
|
||||
@@ -66,6 +69,7 @@ export async function getItemById(db: Db, userId: number, id: number) {
|
||||
${items.priceCents}
|
||||
)`.as("price_cents"),
|
||||
purchasePriceCents: items.purchasePriceCents,
|
||||
quantity: items.quantity,
|
||||
categoryId: items.categoryId,
|
||||
notes: items.notes,
|
||||
productUrl: items.productUrl,
|
||||
@@ -75,10 +79,16 @@ export async function getItemById(db: Db, userId: number, id: number) {
|
||||
)`.as("image_filename"),
|
||||
imageSourceUrl: items.imageSourceUrl,
|
||||
globalItemId: items.globalItemId,
|
||||
brand: sql<
|
||||
string | null
|
||||
>`COALESCE(${globalItems.brand}, ${items.brand})`.as("brand"),
|
||||
createdAt: items.createdAt,
|
||||
updatedAt: items.updatedAt,
|
||||
categoryName: categories.name,
|
||||
categoryIcon: categories.icon,
|
||||
})
|
||||
.from(items)
|
||||
.innerJoin(categories, eq(items.categoryId, categories.id))
|
||||
.leftJoin(globalItems, eq(items.globalItemId, globalItems.id))
|
||||
.where(and(eq(items.id, id), eq(items.userId, userId)));
|
||||
|
||||
@@ -143,6 +153,7 @@ export async function updateItem(
|
||||
imageSourceUrl: string;
|
||||
globalItemId: number;
|
||||
purchasePriceCents: number;
|
||||
brand: string;
|
||||
}>,
|
||||
) {
|
||||
// Check if item exists and belongs to user
|
||||
|
||||
@@ -12,6 +12,7 @@ export const createItemSchema = z.object({
|
||||
quantity: z.number().int().positive().optional(),
|
||||
globalItemId: z.number().int().positive().optional(),
|
||||
purchasePriceCents: z.number().int().nonnegative().optional(),
|
||||
brand: z.string().optional(),
|
||||
});
|
||||
|
||||
export const updateItemSchema = createItemSchema.partial().extend({
|
||||
|
||||
Reference in New Issue
Block a user