# Fase 2 — Compartir proyectos y socios reales (diseño)

Este es el diseño de la Fase 2 sobre el backend que ya tenés (Node + tu PostgreSQL). **Es diseño, no código todavía**: la recomendación es construirlo recién cuando la Fase 1 esté andando y probada en tu entorno. El esquema de la Fase 1 ya quedó preparado para esto (cada proyecto tiene `id` propio y `owner_id`), así que no hay que rehacer nada.

## Qué resuelve

Hoy cada proyecto es de una sola persona. La Fase 2 permite:

- **Invitar a alguien a un proyecto** por email, con un rol: editor (puede modificar) o lector (solo ve).
- Que esa persona vea el proyecto compartido en su propia herramienta y, según el rol, lo edite.
- **Vincular los socios** (que hoy son solo nombres) a cuentas reales. Ese es el puente hacia la Fase 3, donde el reparto se vuelve saldos de tokens transferibles.

## Modelo de datos (lo nuevo)

Se agregan dos tablas. El esquema de proyectos no cambia.

```sql
-- Quién participa de cada proyecto y con qué rol.
create table if not exists project_members (
  project_id uuid not null references projects (id) on delete cascade,
  user_id    uuid not null references app_users (id) on delete cascade,
  role       text not null check (role in ('owner','editor','viewer')),
  created_at timestamptz not null default now(),
  primary key (project_id, user_id)
);
create index if not exists project_members_user_idx on project_members (user_id);

-- Invitaciones a emails que todavía no tienen cuenta (o que aún no aceptaron).
create table if not exists project_invites (
  id          uuid primary key default gen_random_uuid(),
  project_id  uuid not null references projects (id) on delete cascade,
  email       text not null,
  role        text not null check (role in ('editor','viewer')),
  token       text not null unique,           -- código secreto del link de invitación
  invited_by  uuid references app_users (id) on delete set null,
  created_at  timestamptz not null default now(),
  accepted_at timestamptz
);
create index if not exists project_invites_email_idx on project_invites (lower(email));

-- Migración: el dueño de cada proyecto existente pasa a ser "owner" en la tabla de miembros.
insert into project_members (project_id, user_id, role)
  select id, owner_id, 'owner' from projects
  on conflict do nothing;
```

La regla de acceso pasa de *"sos el dueño"* a *"sos miembro"*: podés **ver** un proyecto si hay una fila tuya en `project_members`; podés **editar** si tu rol es `owner` o `editor`.

## API (lo nuevo)

Cambia una ruta y se agregan otras:

- `GET /api/projects` — ahora devuelve **los tuyos y los compartidos**, cada uno con su `id` de servidor, tu `role`, y el email del dueño. El cliente los distingue para marcar cuáles son compartidos.
- `PUT /api/projects/by-id/:projectId` — guardar un proyecto compartido por su id de servidor (valida que tengas rol `owner`/`editor`). Los proyectos propios siguen guardándose como en Fase 1.
- `POST /api/projects/:projectId/members` — invitar `{ email, role }` (solo el dueño). Si el email ya tiene cuenta, crea la membresía directo; si no, crea una invitación con un token y un link para compartir.
- `GET /api/projects/:projectId/members` — listar miembros (cualquier miembro).
- `DELETE /api/projects/:projectId/members/:userId` — quitar a alguien (solo el dueño).
- `POST /api/invites/accept` — aceptar `{ token }` estando logueado: te suma como miembro y consume la invitación.

Toda ruta valida permiso en el servidor según `project_members`. **El cliente nunca decide su propio rol**: lo dice la base.

## Cambios en la herramienta (cliente)

- Cada proyecto guardado en el navegador suma `serverId` (el `id` del proyecto en la base) y `role`. La sincronización trae dos grupos: *los míos* y *los compartidos conmigo*. Editar uno compartido empuja por `serverId` a la ruta nueva.
- Botón **Compartir** en la vista de proyecto: abre un panel con la lista de miembros y un campo para invitar por email con rol. Si la persona no tiene cuenta, se muestra un link de invitación para pasarle.
- En el selector de proyectos y en la **Cartera**, los compartidos se marcan con una etiqueta ("compartido por …") y, si tu rol es lector, los campos se muestran en modo solo-lectura.
- En la pestaña **Socios**, cada socio podrá vincularse opcionalmente a un email de miembro. Eso no cambia el cálculo, pero deja la base lista para la Fase 3 (esos vínculos se convierten en los titulares de los tokens).

## Seguridad

- Permisos siempre verificados en el servidor contra `project_members`; el rol que mande el cliente se ignora.
- Las invitaciones viajan con un token aleatorio imposible de adivinar; aceptarla vincula el email a la cuenta que la acepta.
- Quitar a un miembro corta su acceso de inmediato (la siguiente sincronización ya no le trae el proyecto).

## Decisiones abiertas para definir antes de construir

1. **Conflictos de edición simultánea.** Hoy usamos "el último que guarda gana", que alcanza para una persona en varios dispositivos. Con dos personas editando a la vez conviene, al menos, avisar cuando la versión del servidor es más nueva que la local. ¿Lo dejamos simple o sumamos ese aviso?
2. **Envío de invitaciones.** ¿Alcanza con generar un **link** para que vos se lo pases a la persona (cero infraestructura), o querés **envío de emails automático** (requiere un servicio de correo y algo más de configuración)?
3. **Permisos finos.** ¿Tres roles (dueño / editor / lector) son suficientes para empezar, o ya necesitás algo más granular?

## Orden recomendado

1. Encendé y probá la **Fase 1** en tu PostgreSQL (crear cuenta, guardar, abrir desde otro dispositivo).
2. Definimos las tres decisiones abiertas de arriba.
3. Construyo la Fase 2: tablas, rutas nuevas y los cambios de cliente (botón Compartir, proyectos compartidos, vínculo socio↔cuenta).
4. Recién con eso firme, encaramos la Fase 3 (tokens transferibles), que es la más delicada porque mueve propiedad.
