Nexus Mode

A modern project management tool built with Next.js.

Product

  • Features
  • Pricing

Resources

  • Documentation
  • GitHub

Legal

  • Terms of Service
  • Contact

© ... All rights reserved.

2026
Home
DocsFeaturesPricingServicesContact
Sign InSign Up
Getting StartedAPI ReferencePermissionsExamplesFAQ
docs/examples
Getting StartedAPI ReferencePermissionsExamplesFAQ

Examples

Business logic and code examples

1. Self-serve signup (sole proprietor: corporate + manager)

Anyone can Sign up without an admin. The app provisions a linked workspace: one shared id for corporates and managers (with managers.corp_id equal to that id), plus a default team, via provisionSoloManagerWorkspace in lib/data/linked-user-workspace.ts, called from signUp in actions/auth.ts. Caps for solo workspaces live in lib/workspace/solo.ts (default 2 corp-users total / 2 per team; Pro via Admin raises max_teams and caps to 40 total / per team).

  • After signup, GET /api/confirm-email?token=... confirms the manager row, then creates a session with managerId, corpId, and corpName.
  • Sign in resolves a confirmed manager by email, else a corp-user—one email/password form (signIn in actions/auth.ts).
  • Account: /dashboard/settings and changeUserPasswordAction update managers or corp_users.

2. Corporate and manager registration (admin path)

Separately, corporates can be created by an admin. Managers are invited by the admin and complete registration via an email link.

Creating a corporate (admin)

The admin goes to Dashboard → Admin → Corporates → Add Corporate and submits the form (name, address, phone). The server action createCorporateAction in actions/corporates.ts runs:

  • requireAdmin() ensures only an admin can call it.
  • Form data is validated with CorporateSchema (name, address, phone).
  • dbCreateCorporate (from lib/data/corporates.ts) inserts a new row into the corporates table with a generated nanoid(12).
  • Redirects to /dashboard/admin/corporates.

Inviting a manager (admin)

From the corporates list, the admin clicks Add Manager for a corporate and enters the manager’s email. The action inviteManagerAction in actions/managers.ts:

  • Checks admin and that the corporate exists; ensures no existing manager with that email for that corporate.
  • Generates a temporary password and a confirmation token (24h expiry).
  • createManager in lib/data/managers.ts inserts into managers (id, email, hashed password, corpId, emailConfirmationToken, emailConfirmationTokenExpiresAt).
  • Sends an invitation email with a link like /register/manager?token=....
  • A cron job can call POST /api/clean-managers-table to delete unconfirmed managers after 24h.

Manager completes registration

The manager opens the link and lands on /register/manager. The form is pre-filled with email and corporate (from GET /api/validate-manager-token?token=...). On submit, registerManagerAction in actions/manager-registration.ts:

  • Validates token via getManagerByConfirmationToken; rejects if invalid or email mismatch.
  • Hashes the new password and calls updateManagerPassword (updates only password, keeps token for email confirmation).
  • Sends a confirmation email with a link to GET /api/confirm-manager-email?token=....

When the manager clicks the confirmation link, the API validates the token, sets emailConfirmed, creates a session (managerId, corpId, corpName), and redirects to /dashboard/corporate.

3. Corporate user registration and team assignment

Corporate users (team members) are invited by a manager and are assigned to a team at invitation time. One corp-user can belong to multiple teams (many-to-many via teams_corp_users).

Manager invites a corp-user

The manager goes to Dashboard → Corporate → Corp Users → Add Corp User (or /dashboard/corporate/corp-users/new). They must have at least one team (enforced in the page). The form collects email and team. The action inviteCorpUserAction in actions/corp-users.ts:

  • Requires session.managerId.
  • Validates email, corpId, teamId; ensures the team belongs to the current manager and the corporate exists.
  • Ensures no existing corp-user with that email (in the app, uniqueness is by email across the corporate).
  • createCorpUser in lib/data/corp-users.ts inserts into corp_users (temp password, confirmation token, 24h expiry).
  • addCorpUserToTeam(teamId, corpUserId) in lib/data/teams.ts inserts into teams_corp_users, so the user is assigned to the chosen team at creation.
  • Sends invitation email with link to /register/corp-user?token=.... Confirmation link is GET /api/confirm-corp-user-email?token=... → sets email confirmed, creates session, redirects to /dashboard/issues.

4. Manager: updating teams and users

From Dashboard → Corporate, the manager has a Teams tab and a Corp Users tab. Teams are created, edited, and deleted; users can be added to teams.

Teams: create, update, delete

All team actions live in actions/teams.ts and require either a manager session or admin.

  • Create: createTeamAction(corpId, prev, formData) — requires session.managerId, validates name (1–100 chars), then dbCreateTeam in lib/data/teams.ts (name, managerId, corpId). Redirects to /dashboard/corporate.
  • Update: updateTeamAction(teamId, prev, formData) — loads team; only the team’s manager (or admin) can update. dbUpdateTeam(teamId, { name }) updates the name.
  • Delete: deleteTeamAction(teamId) — same auth; dbDeleteTeam(teamId) removes the team (and any junction rows in teams_corp_users by schema/constraints).

Adding a user to a team

On the team edit page (/dashboard/corporate/teams/[id]/edit), the manager sees current members and a dropdown of corp-users not in the team. Selecting one and submitting calls addCorpUserToTeamAction(teamId, corpUserId):

  • Verifies the team belongs to the current manager.
  • Verifies the corp-user belongs to the same corporate (getCorpUsersByCorpId(team.corpId)).
  • dbAddCorpUserToTeam(teamId, corpUserId) inserts into teams_corp_users. The data layer also exposes removeCorpUserFromTeam(teamId, corpUserId) in lib/data/teams.ts for removing a user from a team.

5. Issues: create, assign, filter, update

Issues are usually created in a corporate context: a team (optional for some rows) with a corp-user creator (corpUserId) or a manager creator (createdByManagerId). Personal issues set userId to the owning manager’s id; corporate is derived from that manager’s corp_id for list/detail and getIssueCorporateId in actions). Assignees use assignedCorpUserId / assignedManagerId.

Creating an issue

createIssue(data) in actions/issues.ts validates with IssueSchema: title (3–100), description, status (backlog | todo | in_progress | done), priority (low | medium | high). Then:

  • Corp-user: corpUserId + valid teamId for their teams; session must be that corp-user.
  • Manager: createdByManagerId + teamId the manager owns (getTeamsByManagerId); session must be that manager.

Updating an issue

updateIssue(id, data) first authorizes the caller:

  • Corp-user: authorizeCorpUserToEditIssue(corpUserId, issueId) in lib/data/issues.ts — creator, assignee, or team membership.
  • Manager: updateIssue loads getIssueCorporateId(issueId) and requires it to equal session.corpId (same resolution as the issue detail page, including personal userId issues).
  • Then only title, description, status, and priority are updated (no owner/assignee change here).

Assigning an issue to a user or manager

assignIssueAction(issueId, assigneeType, assigneeId) in actions/issues.ts:

  • Loads the issue; it must have a teamId. Resolves the team’s corpId.
  • Caller must be a manager who owns that team, or a corp-user with edit permission on the issue (authorizeCorpUserToEditIssue).
  • If assigneeType === null or no assigneeId: assignIssue(issueId, null, null) clears assignment.
  • If assigneeType === 'corp_user': assignee must be in getAssignableCorpUsersByCorpId(corpId); then assignIssue(issueId, assigneeId, null) sets assignedCorpUserId.
  • If assigneeType === 'manager': assignee must be in getManagersByCorpId(corpId); then assignIssue(issueId, null, assigneeId) sets assignedManagerId.

Filtering issues

The issues list is built in app/dashboard/issues/page.tsx and filtered in IssuesContent (app/dashboard/issues/IssuesContent.tsx).

  • Corp-user: Tab “Created by me” → getIssuesCreatedByCorpUser(corpUserId); “Assigned to me” → getIssuesAssignedToCorpUser(corpUserId). Optional ?teamId= filters to that team (client-side filter on the fetched list).
  • Manager: Teams from getTeamsByCorpId(session.corpId). With All teams, the server merges per-team issues plus getIssuesWithNoTeamByCorpId (includes no-team rows tied to corp-users/managers in the corp, including issues.user_id personal issues for managers in that corp). With a specific corp-user, getIssuesAssignedToCorpUser plus team / no-team filters. createIssue still requires the manager to own the chosen team via getTeamsByManagerId where enforced.
  • Search: Query param q — client-side filter on issue.title (case-insensitive).
  • Status: Query param status (backlog | todo | in_progress | done) — client-side filter.

6. Admin: all issues (paginated)

Dashboard → Admin → All Issues (/dashboard/admin/issues) loads a page of every issue with getAllIssuesPaginated from lib/data/admin.ts. Query params: page (default 1) and perPage (default 50). Pagination controls live in AdminIssuesList.tsx.

For API endpoints used by these flows (e.g. confirm-email, validate-manager-token, teams), see the API Reference.