docs: consolidate project tracking into PROGRESS.md
- Create PROGRESS.md as single source of truth for status - Slim ROADMAP.md to vision only (~100 lines, down from ~500) - Expand CLAUDE.md with streams, auth routing, forms, workflow - Convert AGENTS.md to stub pointing to CLAUDE.md - Update plan files with status headers, remove progress trackers Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
126
CLAUDE.md
126
CLAUDE.md
@@ -50,6 +50,21 @@ Theme switching is instant via CSS custom property injection (no reload).
|
||||
| `/products/:id` | Product detail |
|
||||
| `/admin/theme` | Theme editor (auth required) |
|
||||
|
||||
## Elixir Guidelines
|
||||
|
||||
- Use `:req` library for HTTP requests (not httpoison, tesla, httpc)
|
||||
- Lists don't support index access (`list[i]`), use `Enum.at/2`
|
||||
- Access changeset fields with `Ecto.Changeset.get_field/2`, not `changeset[:field]`
|
||||
- Preload associations in queries when needed in templates
|
||||
- **Rebinding:** Must bind result of `if`/`case` blocks:
|
||||
```elixir
|
||||
# WRONG - rebinding inside block is lost
|
||||
if connected?(socket), do: socket = assign(socket, :val, val)
|
||||
|
||||
# RIGHT - bind result to variable
|
||||
socket = if connected?(socket), do: assign(socket, :val, val), else: socket
|
||||
```
|
||||
|
||||
## Phoenix 1.8 Guidelines
|
||||
|
||||
- **Always** wrap LiveView templates with `<Layouts.app flash={@flash} ...>`
|
||||
@@ -57,15 +72,24 @@ Theme switching is instant via CSS custom property injection (no reload).
|
||||
- Use `to_form/2` for all form handling, access via `@form[:field]`
|
||||
- Use `<.input>` component from core_components.ex
|
||||
- Use `<.icon name="hero-x-mark">` for icons, not Heroicons modules
|
||||
- Place routes in correct `live_session` scope (`:current_user` or `:require_authenticated_user`)
|
||||
|
||||
## Elixir/Ecto Guidelines
|
||||
### Auth Routing
|
||||
|
||||
- Use `:req` library for HTTP requests (not httpoison, tesla, httpc)
|
||||
- Use streams for collections: `stream(socket, :items, items)`
|
||||
- Lists don't support index access (`list[i]`), use `Enum.at/2`
|
||||
- Access changeset fields with `Ecto.Changeset.get_field/2`, not `changeset[:field]`
|
||||
- Preload associations in queries when needed in templates
|
||||
Routes requiring auth go in `:require_authenticated_user` live_session:
|
||||
```elixir
|
||||
live_session :require_authenticated_user,
|
||||
on_mount: [{SimpleshopThemeWeb.UserAuth, :require_authenticated}] do
|
||||
live "/admin/theme", ThemeLive.Index
|
||||
end
|
||||
```
|
||||
|
||||
Public routes with optional user go in `:current_user` live_session:
|
||||
```elixir
|
||||
live_session :current_user,
|
||||
on_mount: [{SimpleshopThemeWeb.UserAuth, :mount_current_scope}] do
|
||||
live "/", ShopLive.Home
|
||||
end
|
||||
```
|
||||
|
||||
## HEEx Template Guidelines
|
||||
|
||||
@@ -73,9 +97,97 @@ Theme switching is instant via CSS custom property injection (no reload).
|
||||
- Class lists require bracket syntax: `class={["base", @cond && "extra"]}`
|
||||
- Use `<%!-- comment --%>` for template comments
|
||||
- Never use `else if` or `elsif` - use `cond` or `case`
|
||||
- Use `phx-no-curly-interpolation` for literal braces in code blocks
|
||||
- **Never** use `<% Enum.each %>` - always use `<%= for item <- @items do %>`
|
||||
|
||||
## LiveView Guidelines
|
||||
|
||||
### Streams (Required for Collections)
|
||||
|
||||
Always use streams for lists to prevent memory issues:
|
||||
```elixir
|
||||
# Mount
|
||||
socket |> stream(:products, Products.list_products())
|
||||
|
||||
# Add item
|
||||
socket |> stream_insert(:products, new_product)
|
||||
|
||||
# Reset (e.g., filtering)
|
||||
socket |> stream(:products, filtered_list, reset: true)
|
||||
|
||||
# Delete
|
||||
socket |> stream_delete(:products, product)
|
||||
```
|
||||
|
||||
Template pattern:
|
||||
```heex
|
||||
<div id="products" phx-update="stream">
|
||||
<div :for={{dom_id, product} <- @streams.products} id={dom_id}>
|
||||
{product.title}
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
Empty state with Tailwind:
|
||||
```heex
|
||||
<div id="products" phx-update="stream">
|
||||
<div class="hidden only:block">No products yet</div>
|
||||
<div :for={{dom_id, product} <- @streams.products} id={dom_id}>...</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Form Handling
|
||||
|
||||
From params:
|
||||
```elixir
|
||||
def handle_event("validate", %{"product" => params}, socket) do
|
||||
{:noreply, assign(socket, form: to_form(params, as: :product))}
|
||||
end
|
||||
```
|
||||
|
||||
From changeset:
|
||||
```elixir
|
||||
changeset = Product.changeset(%Product{}, params)
|
||||
socket |> assign(form: to_form(changeset))
|
||||
```
|
||||
|
||||
### Gotchas
|
||||
|
||||
- `phx-hook` with DOM manipulation requires `phx-update="ignore"`
|
||||
- Avoid LiveComponents unless you have a specific need (isolated state, targeted updates)
|
||||
- Never use deprecated `phx-update="append"` or `phx-update="prepend"`
|
||||
|
||||
## JS/CSS Guidelines
|
||||
|
||||
- Tailwind v4 uses `@import "tailwindcss"` syntax (no tailwind.config.js)
|
||||
- **Never** use `@apply` in CSS
|
||||
- **Never** write inline `<script>` tags - use hooks in assets/js/
|
||||
- All vendor deps must be imported into app.js/app.css
|
||||
|
||||
## LiveView Testing
|
||||
|
||||
- Use `element/2`, `has_element/2` - never test raw HTML
|
||||
- Reference DOM IDs from templates in tests
|
||||
- Debug with LazyHTML: `LazyHTML.filter(document, "selector")`
|
||||
|
||||
## Documentation Workflow
|
||||
|
||||
**Single source of truth:** [PROGRESS.md](PROGRESS.md)
|
||||
- Update after completing any feature or task
|
||||
- Contains current status, next steps, and task breakdown
|
||||
- Link to plan files for implementation details
|
||||
|
||||
**Plan files** (docs/plans/*.md):
|
||||
- Implementation references, not status trackers
|
||||
- Mark status at top (e.g., "Status: Complete")
|
||||
- Keep detailed architecture/design decisions
|
||||
|
||||
**Task sizing:**
|
||||
- Break features into ~1-2 hour tasks
|
||||
- Each task should fit in one Claude session without context overflow
|
||||
- Include: files to modify, acceptance criteria, estimate
|
||||
|
||||
**Before starting work:**
|
||||
1. Check PROGRESS.md for current status and next task
|
||||
2. Read relevant plan file for implementation details
|
||||
3. Focus on one task at a time
|
||||
|
||||
Reference in New Issue
Block a user