Skip to content

Commit 8946c4b

Browse files
etewiahclaude
andcommitted
Docs: Add AI-generated codebase analysis and architecture documentation
These new Markdown files, generated by Claude AI, provide comprehensive insights into the PropertyWebBuilder codebase. They cover: - Detailed architectural patterns and design decisions. - Feature completeness audit and implementation specifics. - Test coverage assessment and testing strategies. - Security considerations and potential improvements. - Performance optimization guidelines. - Actionable improvement recommendations and a quick checklist. The documentation is organized under and and serves as a foundational reference for understanding, maintaining, and evolving the application. Co-authored-by: Claude <noreply@anthropic.com>
1 parent 27843b0 commit 8946c4b

14 files changed

+7476
-0
lines changed

docs/architecture/listing_types_overview.md

Lines changed: 519 additions & 0 deletions
Large diffs are not rendered by default.

docs/architecture/url_routing_comparison.md

Lines changed: 636 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 389 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,389 @@
1+
# URL Routing Quick Reference
2+
3+
## TL;DR Comparison Table
4+
5+
| Aspect | Internal Listings | External Listings |
6+
|--------|-------------------|-------------------|
7+
| **URL Pattern** | `/properties/for-{type}/{id}/{title}` | `/external_listings/{reference}` |
8+
| **Controller** | `Pwb::PropsController` | `Pwb::Site::ExternalListingsController` |
9+
| **Primary ID** | UUID or Slug | Provider Reference Code |
10+
| **Route Name** | `prop_show_for_rent` / `prop_show_for_sale` | `external_listing_path` |
11+
| **Find By** | Slug first, then UUID | Reference code directly |
12+
| **Example URL** | `/en/properties/for-rent/abc-uuid/my-apartment` | `/en/external_listings/EXT-REF-12345` |
13+
| **Data Source** | Database | External feed provider |
14+
| **Editable** | Yes (admin interface) | No (read-only) |
15+
| **Listing Type** | Separate routes per type | Same route, type in data/query |
16+
17+
---
18+
19+
## URL Generation Examples
20+
21+
### Internal Properties
22+
23+
```ruby
24+
# From ListedProperty model
25+
property.contextual_show_path("for_rent")
26+
# => "/en/properties/for-rent/luxury-villa-slug/luxury-villa-name"
27+
28+
# Using route helper
29+
prop_show_for_rent_path(
30+
locale: :en,
31+
id: "luxury-villa-slug",
32+
url_friendly_title: "luxury-villa-name"
33+
)
34+
35+
# Backward compatible (using ID instead of slug)
36+
prop_show_for_rent_path(
37+
locale: :en,
38+
id: "550e8400-e29b-41d4-a716-446655440000",
39+
url_friendly_title: "luxury-villa-name"
40+
)
41+
```
42+
43+
### External Properties
44+
45+
```ruby
46+
# Using route helper
47+
external_listing_path(
48+
reference: "EXT-REF-12345",
49+
listing_type: :sale,
50+
locale: :en
51+
)
52+
# => "/en/external_listings/EXT-REF-12345?listing_type=sale"
53+
54+
# Without listing_type (often implicit in data)
55+
external_listing_path(reference: "EXT-REF-12345")
56+
# => "/external_listings/EXT-REF-12345"
57+
```
58+
59+
---
60+
61+
## In Views - Common Patterns
62+
63+
### Linking to Internal Property
64+
65+
```erb
66+
<%# Method 1: Using model helper %>
67+
<a href="<%= @property.contextual_show_path("for_rent") %>">
68+
View Property
69+
</a>
70+
71+
<%# Method 2: Direct route helper %>
72+
<%= link_to "View", prop_show_for_rent_path(
73+
id: @property.slug_or_id,
74+
url_friendly_title: @property.url_friendly_title
75+
) %>
76+
```
77+
78+
### Linking to External Property
79+
80+
```erb
81+
<%# Using external_listing_path helper %>
82+
<a href="<%= external_listing_path(reference: @property.reference, listing_type: @property.listing_type) %>">
83+
View Property
84+
</a>
85+
86+
<%# Or using link_to %>
87+
<%= link_to "View", external_listing_path(reference: @property.reference) %>
88+
```
89+
90+
### Mixed Provider Context (Saved Properties)
91+
92+
```erb
93+
<% if saved_property.provider == "internal" %>
94+
<a href="<%= prop_show_for_rent_path(id: saved_property.id, url_friendly_title: saved_property.url_friendly_title) %>">
95+
View Internal
96+
</a>
97+
<% else %>
98+
<a href="<%= external_listing_path(reference: saved_property.external_reference) %>">
99+
View External
100+
</a>
101+
<% end %>
102+
```
103+
104+
---
105+
106+
## Controller Actions Quick Reference
107+
108+
### Internal Listings (`PropsController`)
109+
110+
```ruby
111+
GET /properties/for-rent/:id/:url_friendly_title
112+
→ props#show_for_rent
113+
Looks up by slug, falls back to UUID
114+
115+
GET /properties/for-sale/:id/:url_friendly_title
116+
→ props#show_for_sale
117+
Looks up by slug, falls back to UUID
118+
```
119+
120+
### External Listings (`ExternalListingsController`)
121+
122+
```ruby
123+
GET /external_listings
124+
→ external_listings#index
125+
Lists all or searches
126+
127+
GET /external_listings/search
128+
→ external_listings#search
129+
Same as index with search semantics
130+
131+
GET /external_listings/:reference
132+
→ external_listings#show
133+
Finds by reference code
134+
135+
GET /external_listings/:reference/similar
136+
→ external_listings#similar
137+
Gets similar properties
138+
139+
GET /external_listings/locations (JSON)
140+
→ external_listings#locations
141+
142+
GET /external_listings/property_types (JSON)
143+
→ external_listings#property_types
144+
145+
GET /external_listings/filters (JSON)
146+
→ external_listings#filters
147+
```
148+
149+
---
150+
151+
## Lookup Strategies
152+
153+
### Internal Properties - Lookup Logic
154+
155+
```ruby
156+
def find_property_by_slug_or_id(identifier)
157+
scope = Pwb::ListedProperty.where(website_id: @current_website.id)
158+
159+
# Step 1: Try slug
160+
property = scope.find_by(slug: identifier)
161+
return property if property
162+
163+
# Step 2: Fall back to ID (UUID or integer)
164+
scope.find_by(id: identifier)
165+
end
166+
```
167+
168+
**Priority:**
169+
1. Slug (if present in database)
170+
2. UUID/ID (fallback for legacy URLs)
171+
172+
### External Properties - Lookup Logic
173+
174+
```ruby
175+
def set_listing
176+
@listing = external_feed.find(
177+
params[:reference],
178+
locale: I18n.locale,
179+
listing_type: listing_type_param
180+
)
181+
end
182+
```
183+
184+
**Priority:**
185+
1. Reference code (provider-assigned)
186+
2. No fallback - exact match required
187+
188+
---
189+
190+
## Locale Handling
191+
192+
Both listing types support locale prefixes:
193+
194+
```
195+
Internal:
196+
/en/properties/for-rent/abc-uuid/title
197+
/es/properties/for-rent/abc-uuid/titulo
198+
/fr/properties/for-rent/abc-uuid/titre
199+
200+
External:
201+
/en/external_listings/EXT-REF-123
202+
/es/external_listings/EXT-REF-123
203+
/fr/external_listings/EXT-REF-123
204+
```
205+
206+
Use `I18n.locale` to generate locale-aware URLs:
207+
208+
```ruby
209+
# Internal
210+
prop_show_for_rent_path(id: id, url_friendly_title: title, locale: I18n.locale)
211+
212+
# External
213+
external_listing_path(reference: ref, locale: I18n.locale)
214+
```
215+
216+
---
217+
218+
## URL Component Breakdown
219+
220+
### Internal Property URL
221+
222+
```
223+
/en/properties/for-rent/luxury-villa-slug/luxury-villa-barcelona
224+
│ │ │ │ │
225+
│ │ │ │ └─ URL-friendly title (SEO, not used for lookup)
226+
│ │ │ └─ Slug or UUID (used for lookup)
227+
│ │ └─ Listing type indicator (in path)
228+
│ └─ Listing type (for-rent or for-sale)
229+
└─ Locale (optional prefix)
230+
```
231+
232+
### External Property URL
233+
234+
```
235+
/en/external_listings/EXT-REF-12345
236+
│ │ │
237+
│ │ └─ Reference code (used for lookup)
238+
│ └─ Collection name (external_listings)
239+
└─ Locale (optional prefix)
240+
```
241+
242+
---
243+
244+
## Parameter Differences
245+
246+
### Internal Properties - URL Parameters
247+
248+
```ruby
249+
prop_show_for_rent_path(
250+
locale: :en, # Locale code
251+
id: slug_or_id, # Slug preferred, UUID fallback
252+
url_friendly_title: "my-property" # For SEO only
253+
)
254+
255+
# Only 'id' is used for database lookup
256+
# 'url_friendly_title' is for SEO and user readability
257+
# 'locale' changes the URL prefix
258+
```
259+
260+
### External Properties - URL Parameters
261+
262+
```ruby
263+
external_listing_path(
264+
reference: "EXT-REF-123", # Required, used for lookup
265+
listing_type: :sale, # Optional, can be in URL or query string
266+
locale: :en # Locale code
267+
)
268+
269+
# All parameters except 'locale' can vary by provider
270+
# Usually: reference is required, others are optional/contextual
271+
```
272+
273+
---
274+
275+
## SEO Considerations
276+
277+
### Internal Properties
278+
279+
- **Slug in URL:** Use custom slug for better SEO
280+
- **Title in URL:** Automatically added, helps with rankings
281+
- **Canonical URL:** Generated to avoid duplicates
282+
- **noindex support:** Can flag archived/reserved properties
283+
284+
**Best Practice:**
285+
```ruby
286+
# Create a good slug
287+
property.update(slug: "luxury-beachfront-villa-marbella")
288+
289+
# This generates SEO-friendly URL:
290+
/en/properties/for-sale/luxury-beachfront-villa-marbella/luxury-beachfront-villa-marbella
291+
```
292+
293+
### External Properties
294+
295+
- **Reference in URL:** Not SEO-friendly but necessary
296+
- **Title not in URL:** Less optimal for SEO
297+
- **Dynamic canonical:** Prevents duplicate content
298+
299+
**Improvement Opportunity:**
300+
- Consider adding provider + title to external listing URLs for better SEO
301+
302+
---
303+
304+
## Migration/Compatibility Notes
305+
306+
### Old vs New Internal URLs
307+
308+
```ruby
309+
# Old (legacy, still works with ID fallback)
310+
/properties/for-rent/12345/my-property
311+
312+
# New (with slug)
313+
/properties/for-rent/my-property-slug/my-property
314+
315+
# Both work because of find_property_by_slug_or_id logic
316+
```
317+
318+
### External Listings
319+
320+
```ruby
321+
# Current implementation
322+
/external_listings/EXT-REF-12345
323+
324+
# Could be enhanced to:
325+
/external_listings/EXT-REF-12345/property-title
326+
# But would require route and model changes
327+
```
328+
329+
---
330+
331+
## Common Issues and Solutions
332+
333+
### Issue: URL returns 404 for valid property
334+
335+
**Internal Properties:**
336+
- Check if property is marked `visible: true`
337+
- Check if property has correct `for_rent` or `for_sale` flag
338+
- Verify slug exists in database or fallback to ID works
339+
340+
**External Properties:**
341+
- Verify feed is `enabled` and `configured`
342+
- Check reference code matches exactly (case-sensitive)
343+
- Verify `listing_type` matches available data
344+
345+
### Issue: Slug not being used in URL
346+
347+
**Solution:**
348+
- Ensure slug is populated in database
349+
- Check: `property.slug.present?` returns true
350+
- Verify slug doesn't exceed 255 characters
351+
352+
### Issue: External listing not appearing in search
353+
354+
**Solution:**
355+
- Verify external feed provider is configured
356+
- Check feed is returning results: `external_feed.configured?` && `external_feed.enabled?`
357+
- Verify search parameters are correct
358+
359+
---
360+
361+
## Testing Checklist
362+
363+
- [ ] Internal property loads with slug
364+
- [ ] Internal property loads with UUID (fallback)
365+
- [ ] External property loads with reference
366+
- [ ] Locale prefixes work for both types
367+
- [ ] SEO meta tags are correct for both types
368+
- [ ] Canonical URLs are generated correctly
369+
- [ ] 404 handling works for invalid identifiers
370+
- [ ] Mixed provider scenarios (saved properties) work
371+
- [ ] Search filters work for external listings
372+
- [ ] HTTP caching headers are set appropriately
373+
374+
---
375+
376+
## File References
377+
378+
| File | Purpose |
379+
|------|---------|
380+
| `/config/routes.rb` | Route definitions |
381+
| `/app/controllers/pwb/props_controller.rb` | Internal listing controller |
382+
| `/app/controllers/pwb/site/external_listings_controller.rb` | External listing controller |
383+
| `/app/models/concerns/listed_property/url_helpers.rb` | URL generation logic |
384+
| `/app/helpers/pwb/search_url_helper.rb` | Search URL helpers |
385+
386+
---
387+
388+
*Quick Reference Version 1.0*
389+
*For detailed information, see url_routing_comparison.md*

0 commit comments

Comments
 (0)