Compare commits
No commits in common. "5894e13d67f59022a96bab4396063720a38fdbe1" and "770b6e84bb828bce2bc7bc5294ce7db77ab4e338" have entirely different histories.
5894e13d67
...
770b6e84bb
3 changed files with 36 additions and 62 deletions
|
@ -45,10 +45,12 @@ model Item {
|
||||||
urlId Int @map("url_id")
|
urlId Int @map("url_id")
|
||||||
wikidataId String? @map("wikidata_id")
|
wikidataId String? @map("wikidata_id")
|
||||||
itemOrder Int @default(0) @map("item_order")
|
itemOrder Int @default(0) @map("item_order")
|
||||||
globalItemId String @map("global_item_id")
|
globalItemId String @unique @map("global_item_id")
|
||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
url Url @relation(fields: [urlId], references: [id], onDelete: Cascade)
|
url Url @relation(fields: [urlId], references: [id], onDelete: Cascade)
|
||||||
|
itemProperties ItemProperty[] @relation("ItemToItemProperty")
|
||||||
|
deletedProperties DeletedProperty[]
|
||||||
|
|
||||||
@@map("items")
|
@@map("items")
|
||||||
}
|
}
|
||||||
|
@ -60,6 +62,7 @@ model ItemProperty {
|
||||||
value String
|
value String
|
||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
|
item Item @relation("ItemToItemProperty", fields: [globalItemId], references: [globalItemId], onDelete: Cascade)
|
||||||
property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)
|
property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@id([globalItemId, propertyId])
|
@@id([globalItemId, propertyId])
|
||||||
|
@ -87,6 +90,7 @@ model DeletedProperty {
|
||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
url Url @relation(fields: [urlId], references: [id], onDelete: Cascade)
|
url Url @relation(fields: [urlId], references: [id], onDelete: Cascade)
|
||||||
|
item Item @relation(fields: [globalItemId], references: [globalItemId], onDelete: Cascade)
|
||||||
property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)
|
property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@id([urlId, globalItemId, propertyId])
|
@@id([urlId, globalItemId, propertyId])
|
||||||
|
|
|
@ -6,12 +6,10 @@ import { Item } from '@/types/database';
|
||||||
// PUT /api/urls/[encodedUrl]/items/[itemId] - Update a specific item
|
// PUT /api/urls/[encodedUrl]/items/[itemId] - Update a specific item
|
||||||
export async function PUT(
|
export async function PUT(
|
||||||
request: NextRequest,
|
request: NextRequest,
|
||||||
{ params }: { params: Promise<{ encodedUrl: string; itemId: string }> }
|
{ params }: { params: { encodedUrl: string; itemId: string } }
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
// Await params before accessing properties
|
const { encodedUrl, itemId } = params;
|
||||||
const resolvedParams = await params;
|
|
||||||
const { encodedUrl, itemId } = resolvedParams;
|
|
||||||
const url = decodeURIComponent(encodedUrl);
|
const url = decodeURIComponent(encodedUrl);
|
||||||
const item: Item = await request.json();
|
const item: Item = await request.json();
|
||||||
|
|
||||||
|
@ -48,16 +46,12 @@ export async function PUT(
|
||||||
return NextResponse.json({ success: true, itemId });
|
return NextResponse.json({ success: true, itemId });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[API] Update error:', error);
|
console.error('[API] Update error:', error);
|
||||||
|
|
||||||
// Await params for error handling too
|
|
||||||
const resolvedParams = await params;
|
|
||||||
|
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{
|
{
|
||||||
error: 'Database error',
|
error: 'Database error',
|
||||||
details: error instanceof Error ? error.message : 'Unknown error',
|
details: error instanceof Error ? error.message : 'Unknown error',
|
||||||
itemId: resolvedParams.itemId,
|
itemId: params.itemId,
|
||||||
url: decodeURIComponent(resolvedParams.encodedUrl)
|
url: decodeURIComponent(params.encodedUrl)
|
||||||
},
|
},
|
||||||
{ status: 500 }
|
{ status: 500 }
|
||||||
);
|
);
|
||||||
|
@ -67,12 +61,10 @@ export async function PUT(
|
||||||
// DELETE /api/urls/[encodedUrl]/items/[itemId] - Delete a specific item
|
// DELETE /api/urls/[encodedUrl]/items/[itemId] - Delete a specific item
|
||||||
export async function DELETE(
|
export async function DELETE(
|
||||||
request: NextRequest,
|
request: NextRequest,
|
||||||
{ params }: { params: Promise<{ encodedUrl: string; itemId: string }> }
|
{ params }: { params: { encodedUrl: string; itemId: string } }
|
||||||
) {
|
) {
|
||||||
// Await params before accessing properties
|
const url = decodeURIComponent(params.encodedUrl);
|
||||||
const resolvedParams = await params;
|
const itemId = params.itemId;
|
||||||
const url = decodeURIComponent(resolvedParams.encodedUrl);
|
|
||||||
const itemId = resolvedParams.itemId;
|
|
||||||
|
|
||||||
console.log('[API] Deleting item', itemId, 'from URL', url);
|
console.log('[API] Deleting item', itemId, 'from URL', url);
|
||||||
|
|
||||||
|
|
|
@ -66,29 +66,9 @@ export async function getItemsByUrl(url: string): Promise<Item[]> {
|
||||||
const items: Item[] = [];
|
const items: Item[] = [];
|
||||||
|
|
||||||
for (const dbItem of urlRecord.items) {
|
for (const dbItem of urlRecord.items) {
|
||||||
// Get core properties (name, description) - these should always be included
|
// Get all properties for this globalItemId
|
||||||
const corePropertyNames = ['name', 'description'];
|
|
||||||
const coreProperties = await tx.property.findMany({
|
|
||||||
where: { name: { in: corePropertyNames } }
|
|
||||||
});
|
|
||||||
const corePropertyIds = coreProperties.map(p => p.id);
|
|
||||||
|
|
||||||
// Get selected custom properties for this URL
|
|
||||||
const selectedPropertiesForUrl = await tx.selectedProperty.findMany({
|
|
||||||
where: { urlId: urlRecord.id },
|
|
||||||
include: { property: true }
|
|
||||||
});
|
|
||||||
const selectedCustomPropertyIds = selectedPropertiesForUrl.map(sp => sp.propertyId);
|
|
||||||
|
|
||||||
// Combine core properties with selected custom properties
|
|
||||||
const allowedPropertyIds = [...corePropertyIds, ...selectedCustomPropertyIds];
|
|
||||||
|
|
||||||
// Get properties for this globalItemId, including core properties and selected custom properties
|
|
||||||
const itemProperties = await tx.itemProperty.findMany({
|
const itemProperties = await tx.itemProperty.findMany({
|
||||||
where: {
|
where: { globalItemId: dbItem.globalItemId },
|
||||||
globalItemId: dbItem.globalItemId,
|
|
||||||
propertyId: { in: allowedPropertyIds }
|
|
||||||
},
|
|
||||||
include: { property: true }
|
include: { property: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -140,13 +120,23 @@ export async function insertItemByUrl(url: string, item: Item): Promise<void> {
|
||||||
|
|
||||||
console.log(`[DB] URL ID: ${urlRecord.id}`);
|
console.log(`[DB] URL ID: ${urlRecord.id}`);
|
||||||
|
|
||||||
// 2. ALWAYS find existing globalItemId by name first (this is the key fix)
|
// 2. Check if this specific item already exists for this URL
|
||||||
|
const existingItem = await tx.item.findUnique({
|
||||||
|
where: { id: item.id }
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Find or create globalItemId based on item name
|
||||||
let globalItemId: string;
|
let globalItemId: string;
|
||||||
|
|
||||||
if (item.name.trim()) {
|
if (existingItem) {
|
||||||
// Look for existing global item with the same name
|
// Use existing globalItemId if item already exists
|
||||||
|
globalItemId = existingItem.globalItemId;
|
||||||
|
console.log(`[DB] Using existing global item ID: ${globalItemId}`);
|
||||||
|
} else {
|
||||||
|
// For new items, find existing globalItemId by name or create new one
|
||||||
const namePropertyId = await getOrCreateProperty(tx, 'name');
|
const namePropertyId = await getOrCreateProperty(tx, 'name');
|
||||||
|
|
||||||
|
// Look for existing global item with the same name
|
||||||
const existingGlobalItem = await tx.itemProperty.findFirst({
|
const existingGlobalItem = await tx.itemProperty.findFirst({
|
||||||
where: {
|
where: {
|
||||||
propertyId: namePropertyId,
|
propertyId: namePropertyId,
|
||||||
|
@ -155,29 +145,25 @@ export async function insertItemByUrl(url: string, item: Item): Promise<void> {
|
||||||
select: { globalItemId: true }
|
select: { globalItemId: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existingGlobalItem) {
|
if (existingGlobalItem && item.name.trim()) {
|
||||||
// Use existing globalItemId for items with the same name
|
// Use existing globalItemId for items with the same name
|
||||||
globalItemId = existingGlobalItem.globalItemId;
|
globalItemId = existingGlobalItem.globalItemId;
|
||||||
console.log(`[DB] Found existing global item ID for name "${item.name}": ${globalItemId}`);
|
console.log(`[DB] Found existing global item ID for name "${item.name}": ${globalItemId}`);
|
||||||
} else {
|
} else {
|
||||||
// Create new globalItemId for new names
|
// Create new globalItemId for new names or empty names
|
||||||
globalItemId = uuidv4();
|
globalItemId = uuidv4();
|
||||||
console.log(`[DB] Created new global item ID for new name "${item.name}": ${globalItemId}`);
|
console.log(`[DB] Created new global item ID: ${globalItemId}`);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// For empty names, always create new globalItemId
|
|
||||||
globalItemId = uuidv4();
|
|
||||||
console.log(`[DB] Created new global item ID for empty name: ${globalItemId}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Get max order for new items
|
// 4. Get max order for new items
|
||||||
const maxOrder = await tx.item.findFirst({
|
const maxOrder = await tx.item.findFirst({
|
||||||
where: { urlId: urlRecord.id },
|
where: { urlId: urlRecord.id },
|
||||||
orderBy: { itemOrder: 'desc' },
|
orderBy: { itemOrder: 'desc' },
|
||||||
select: { itemOrder: true }
|
select: { itemOrder: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
// 4. Upsert the item record (unique by item.id)
|
// 5. Upsert the item record (unique by item.id)
|
||||||
await tx.item.upsert({
|
await tx.item.upsert({
|
||||||
where: { id: item.id },
|
where: { id: item.id },
|
||||||
update: {
|
update: {
|
||||||
|
@ -194,7 +180,7 @@ export async function insertItemByUrl(url: string, item: Item): Promise<void> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 5. Handle all properties (core + custom)
|
// 6. Handle all properties (core + custom)
|
||||||
const allProperties = [
|
const allProperties = [
|
||||||
['name', item.name],
|
['name', item.name],
|
||||||
['description', item.description],
|
['description', item.description],
|
||||||
|
@ -253,18 +239,10 @@ export async function deleteItemByUrl(url: string, itemId: string): Promise<void
|
||||||
where: { id: itemId }
|
where: { id: itemId }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check if this is the last item with this globalItemId
|
// Clean up item properties for this global item
|
||||||
const remainingItems = await tx.item.count({
|
|
||||||
where: { globalItemId: item.globalItemId }
|
|
||||||
});
|
|
||||||
|
|
||||||
// Only delete global properties if this was the last item with this globalItemId
|
|
||||||
if (remainingItems === 0) {
|
|
||||||
await tx.itemProperty.deleteMany({
|
await tx.itemProperty.deleteMany({
|
||||||
where: { globalItemId: item.globalItemId }
|
where: { globalItemId: item.globalItemId }
|
||||||
});
|
});
|
||||||
console.log(`[DB] Cleaned up global properties for globalItemId: ${item.globalItemId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`[DB] Item ${itemId} deleted successfully`);
|
console.log(`[DB] Item ${itemId} deleted successfully`);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue