feat: weighted match scoring, photo upload, property detail page

- scoring.ts: pure scoreMatch + scoreMatchBreakdown with per-criterion weights
- matching.ts: soft scoring (0-100), updates score on re-sync, threshold 20
- search-form-sheet: weight selectors (1-5) per criterion
- customer-search-actions: save/update weight fields
- storage-actions: upload/delete property images to property-images bucket
- storage-utils: getPropertyImagePreviewUrl, parseImageIds helpers
- property-image-uploader: client component with preview grid + delete
- property-form-sheet: integrated image uploader
- properties/[id]: detail page with gallery, specs, matches sidebar
- properties-client: Detay link in dropdown
- matches page: MatchesClient with click-to-breakdown dialog
- sunum page: cover image from first imageIds entry
- matches-client + match-breakdown-dialog: score breakdown per criterion
This commit is contained in:
egecankomur
2026-05-05 19:55:34 +03:00
parent 3d044c5d5b
commit a40e68254b
22 changed files with 1105 additions and 169 deletions
@@ -70,6 +70,11 @@ export async function createCustomerSearchAction(
districts: toJsonList(data.districts),
isActive: true,
notes: data.notes,
priceWeight: data.priceWeight,
m2Weight: data.m2Weight,
locationWeight: data.locationWeight,
roomCountWeight: data.roomCountWeight,
propertyTypeWeight: data.propertyTypeWeight,
createdBy: ctx.user.id,
},
[
@@ -117,6 +122,11 @@ export async function updateCustomerSearchAction(
cities: toJsonList(data.cities),
districts: toJsonList(data.districts),
notes: data.notes,
priceWeight: data.priceWeight,
m2Weight: data.m2Weight,
locationWeight: data.locationWeight,
roomCountWeight: data.roomCountWeight,
propertyTypeWeight: data.propertyTypeWeight,
});
await syncMatchesForSearch(ctx.tenantId, ctx.user.id);