Commit Graph

2 Commits

Author SHA1 Message Date
kovakmedya ab336b191f feat(team): proper member removal + self-leave flow
Member-management UX cleanup:

- Replaced window.confirm() with shadcn Dialog confirmation (matches
  every other destructive action in the app).
- Toast feedback on success/error for both removal and role updates —
  before, errors from the server (örn. 'Sahibi yalnızca başka bir sahip
  kaldırabilir') were swallowed.
- New 'Ayrıl' (leave) button on the current user's own row — previously
  there was no way for a member to leave a workspace except by being
  removed by an admin.

Server (lib/appwrite/team-actions.ts):
- New leaveWorkspaceAction:
  * Refuses if the caller is the only owner (would leave the workspace
    ownerless).
  * Calls teams.deleteMembership for the caller's own membership.
  * Clears account.prefs.activeTenant + isletmem-tenant cookie so the
    next request goes to fallback tenant or onboarding.
  * Audit-logged with self:true marker.
- removeMemberAction unchanged (already had owner-only-can-remove-owner
  + can't-remove-self guards).

UI:
- Each row's action cell now shows: 'Ayrıl' (self) / 'Çıkar' (others, if
  canManage and target isn't owner) / nothing (the rest).
- Removal dialog explains data isn't deleted, just access revoked.
- Leave dialog warns the user about losing access.
- Both dialogs gate close-on-outside-click while a request is in flight.
2026-04-30 08:41:52 +03:00
kovakmedya 643f2de29b feat(team): manual-code invite flow + member management
Multi-tenant invite system without SMTP dependency. Designed for dev/early
stage; promotes to email-driven later by adding SMTP to Appwrite.

New schema:
- invite_links table (code, email, role, status, expiresAt, invitedBy)
  with unique index on code, indexes on (tenantId,status) and (tenantId,email)

New code:
- lib/appwrite/audit.ts: logAudit() helper writes to audit_logs with
  X-Forwarded-For/User-Agent capture; never throws.
- lib/appwrite/tenant-guard.ts: requireTenant() returns
  { user, tenantId, role, settings }; pulls highest role from team
  memberships. requireRole() guard.
- lib/appwrite/team-actions.ts:
  * inviteMemberAction — creates short code (8 char nanoid-style),
    inserts invite_links row with team-scoped perms, returns shortUrl.
    Reuses existing pending invite for same email instead of duplicating.
    Blocks self-invite, blocks invite of existing members.
  * cancelInviteAction — owner/admin only, marks status=cancelled.
  * removeMemberAction — owner/admin only; protects self-removal and
    requires owner-on-owner.
  * updateMemberRoleAction — owner only.
  * resolveInviteCode — public-ish lookup by code (admin SDK).
  * acceptInviteAction — verifies session.email matches invite.email,
    creates membership via admin SDK, marks invite accepted.
  All mutations write to audit_logs.

UI:
- /d/[code] short-URL accept page (server). Logged-in matching user
  sees 'Daveti kabul et' button; non-matching user sees error; logged-out
  user gets sign-up / sign-in CTAs that preserve the code.
- /settings/members page (server): InviteForm, PendingInvitesTable,
  MembersTable. Owner/admin gates respected; only owner can change roles.
- Sign-up and sign-in forms accept ?invite=CODE (and ?email= for sign-up):
  hidden input -> server action redirects to /d/CODE on success.

Other:
- next.config.ts: removed eslint config block (deprecated in Next 16);
  kept typescript.ignoreBuildErrors for template legacy.
2026-04-30 05:34:47 +03:00