hoangsonww / hoangsonww/Boxed-Inventory-App

Collaborators v1 — Share Boxes with Roles, Activity History, and Alerts

Open
#12 0 comments 0 reactions 1 assignee Claimed by @hoangsonww View on GitHub
documentation enhancement good first issue help wanted question
Dominant language
TypeScript
Stars
17
Forks
10
PR merge metrics
No merged PRs in 30d

Description

### Summary

Ship first-class **collaboration** so people can share boxes with roommates/family and keep a lightweight **activity history**. Adds per-box roles (`viewer|editor|owner`), invite links, RLS-safe APIs, and optional notifications.

---

### Why

* Real households aren’t single-user.
* Prevents “who moved my charger?” with a simple audit trail.
* Lays the foundation for real-time presence later.

---

### Scope (MVP)

1. **Per-box roles**

* Respect existing `box_collaborators` (role column: `viewer|editor|owner`).
* RLS: viewers = read-only; editors = CRUD items; owners = manage collaborators & delete.

2. **Invites**

* Owner generates a time-boxed, single-use invite token for a box.
* Recipient accepts to join with `viewer` (owner can upgrade).

3. **Activity history**

* Append-only log for: create/update/delete **box**, **item**, **photo**, **status**, **location**, **collaborator** changes.
* Show human-readable feed on Box page (“Erica changed quantity of ‘Ceramic Mug’ 6 → 8”).

4. **Notifications (optional toggle)**

* Per-box “notify me” switch → in-app toast & bell feed when collaborators edit.

5. **UX**

* “Share” button → modal with role chips, copy invite link, revoke.
* Activity tab (last 50 events), filter by actor/action.
* Badges: `Viewer`, `Editor`, `Owner`.

---

### Acceptance Criteria

* [ ] Viewer cannot mutate box/items via UI **or** API (RLS enforced).
* [ ] Editor can create/edit/delete items; cannot manage collaborators.
* [ ] Owner can transfer ownership, revoke invites, remove collaborators.
* [ ] Invite links expire (default 72h) or upon first redemption.
* [ ] Activity feed renders accurate events with actor + timestamp.
* [ ] “Notify me” shows toasts on collaborator edits within that box.

---

### DB / RLS (TypeORM + SQL)

```sql
-- activity_log
CREATE TABLE activity_log (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
box_id uuid NOT NULL REFERENCES boxes(id) ON DELETE CASCADE,
actor_id uuid NOT NULL REFERENCES profiles(id) ON DELETE SET NULL,
entity_type text NOT NULL, -- 'box' | 'item' | 'photo' | 'collaborator'
entity_id uuid,
action text NOT NULL, -- 'create'|'update'|'delete'|'status_change'|'add'|'remove'
diff jsonb, -- {before:{}, after:{}} minimal snapshot
created_at timestamptz DEFAULT now()
);
CREATE INDEX ON activity_log (box_id, created_at DESC);

-- invites
CREATE TABLE box_invites (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
box_id uuid NOT NULL REFERENCES boxes(id) ON DELETE CASCADE,
created_by uuid NOT NULL REFERENCES profiles(id),
role text NOT NULL CHECK (role IN ('viewer','editor')),
token text UNIQUE NOT NULL,
expires_at timestamptz NOT NULL,
redeemed_by uuid NULL REFERENCES profiles(id),
redeemed_at timestamptz
);
CREATE INDEX ON box_invites (box_id, expires_at);

-- RLS sketch (Postgres)
-- Allow read if owner OR collaborator
-- Allow write only if owner/editor per table and action
```

> Add/adjust RLS policies on `boxes`, `items`, `box_collaborators`, `activity_log`, `box_invites` to enforce roles.

---

### API (examples)

* `POST /api/boxes/:boxId/invites` → {role, expiresAt} → {inviteUrl}
* `POST /api/invites/:token/redeem` → joins as viewer/editor
* `GET /api/boxes/:boxId/collaborators`
* `PATCH /api/boxes/:boxId/collaborators/:profileId` → {role}
* `DELETE /api/boxes/:boxId/collaborators/:profileId`
* `GET /api/boxes/:boxId/activity?limit=50`
* `POST /api/boxes/:boxId/notify/subscribe|unsubscribe`

Hook activity writes from existing item/box mutations (service layer) to keep controllers thin.

---

### Frontend

* **ShareModal**: list collaborators, role dropdown, “Copy link”, “Revoke all invites”.
* **ActivityFeed**: grouped by day, simple icons per action, diff tooltips.
* **RoleGuard**: tiny utility to gate buttons/menus by role.
* **Toasts/Bell**: per-box notifications (local storage flag; later server push).

---

### Implementation Notes

* Tokens: 32+ random bytes base64url; never expose raw IDs inside token.
* Invites: single-use default; also support manual revoke.
* Diff: only store changed keys to keep rows small.
* Performance: index `activity_log(box_id, created_at desc)` for fast feeds.
* Privacy: don’t dump large photo URLs into `diff`; store IDs only.

---

### Risks & Mitigations

* **RLS mistakes** → add unit + integration tests for each role/action.
* **Invite leakage** → short expiry, single-use, easy revoke, audit trail.
* **Noisy feed** → collapse repetitive events (e.g., multiple quantity tweaks in 5m).

---

### More Features

* Real-time presence cursors + live co-editing.
* Email/WebPush notifications.
* Per-folder (collection) sharing beyond single boxes.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.