Skip to content

Teams UI

Frontend implementation of the Teams module and its cross-module integration with the Administrators form.

Routes & Entry Point

The Teams module is registered as a top-level dashboard route:

js
// frontend/client/src/routes/index.js
{ prefix: '/teams', layout: 'dashboard', children: [''] }

The sidebar item is driven by the database row catalog_admin_permissions.id = 281:

FieldValue
actionteams
langmenu.teams
is_menu1
parent_id0 (top-level)
active1

Any role granted this permission will see the Equipos item in the sidebar at the same level as Administrators and Roles.

File Tree

frontend/client/src/
├── routes/index.js                         ← /teams route registration
├── services/
│   └── teams.service.js                    ← Axios service (CRUD)
└── views/
    ├── teams/
    │   ├── index.vue                        ← Page root: catalog loading, provide(), layout
    │   └── components/
    │       ├── Table.vue                    ← DataTable with filters and row actions
    │       ├── CreateTeam.modal.vue         ← Offcanvas: create form
    │       └── EditTeam.modal.vue           ← Offcanvas: edit form (includes active field)
    └── administratorsV2/
        ├── index.vue                        ← Loads teamsCatalog, provides it to admin modals
        └── components/administrators/
            ├── CreateAdministrator.modal.vue ← Consumes teamsCatalog; team_id required
            └── EditAdministrator.modal.vue   ← Consumes teamsCatalog; team_id required

Component Responsibilities

views/teams/index.vue

The page root. Responsibilities:

  • Fetches departmentsCatalog (GET /catalog/departments) and administratorsCatalog (GET /catalog/administrators?active=true) on mount.
  • provide()s both catalogs so child components can inject() them without prop drilling.
  • Renders SectionTitle with the Create button (visible only when auth.can('teams-add')).
  • Hosts TeamsTable and CreateTeamModal refs.
  • Exposes reloadTeamsData(): reloads the table and re-fetches catalogs, so that a newly created team leader appears in the dropdown immediately.
  • Handles the @reload-catalogs event emitted by TeamsTable and the @reload-table event emitted by CreateTeamModal.

views/teams/components/Table.vue

The paginated team list. Responsibilities:

  • Calls api.teams.getTeams(filters) through the DataTable loadData prop.
  • Applies normalizeActive(value) to every row's active field before rendering, to handle MySQL's BIT(1)Buffer edge case:
js
const normalizeActive = (value) => {
    if (typeof value === 'number') return value;
    if (typeof value === 'boolean') return value ? 1 : 0;
    if (typeof value === 'string') return Number(value);
    if (Array.isArray(value?.data)) return Number(value.data[0] ?? 0);
    return value ? 1 : 0;
};
  • Renders the active column using the Status component (success / danger bg-class).
  • Filters: searchMultiple (id / name), select2 for department and leader, select for status.
  • Row actions: Edit (opens EditTeamModal) and Delete (Swal confirmation then api.teams.deleteTeam), both guarded by auth.can('teams-edit').
  • Emits reload-catalogs after any mutation so the parent re-fetches the catalog used by the Administrators form.

views/teams/components/CreateTeam.modal.vue

Create panel. Responsibilities:

  • injects departmentsCatalog and administratorsCatalog from the parent.
  • Uses Offcanvas + FormGenerator with fields: name, description, admin_id (required), department_id.
  • Calls api.teams.createTeam(form.value.state) on save.
  • Emits reload-table on success so the parent reloads the table and catalogs.

views/teams/components/EditTeam.modal.vue

Edit panel. Same structure as CreateTeam.modal.vue plus:

  • Includes an active field (select: Active / Inactive) — required.
  • Receives the team row via show(item), which pre-populates base state (including Number(item.active) to guarantee a numeric value for the select).
  • Calls api.teams.updateTeam(editingId.value, form.value.state) on save.

Service

frontend/client/src/services/teams.service.js wraps all four API calls:

MethodHTTPEndpoint
getTeams(params)GET/teams
createTeam(payload)POST/teams
updateTeam(id, payload)PUT/teams/{id}
deleteTeam(id)DELETE/teams/{id}

All responses go through the global Axios afterResponse: (r) => r.data interceptor, so the service returns the response body directly.

Cross-Module Integration: Administrator Form

The Administrators module (administratorsV2/index.vue) loads the teams catalog via GET /catalog/teams and exposes it to its child modals via provide('teamsCatalog', teamsCatalog).

Both CreateAdministrator.modal.vue and EditAdministrator.modal.vue:

  1. inject('teamsCatalog', ref([])) to access the list.
  2. Render a required team_id select field.
  3. Use a computed (selectedTeam) to find the full team object matching the current form.state.team_id.
  4. watch(selectedTeam) to auto-fill form.state.department_id with selectedTeam.department_id whenever the user changes the team selection.
  5. Set the department_id field as disabled: !!selectedTeam.value and rules: { required: !selectedTeam.value } so it is read-only and not user-editable while a team is selected.

This means the department is always consistent with the selected team and requires no manual input from the user.

Permission Gates

GateComponentCheck
Show Create buttonviews/teams/index.vueauth.can('teams-add')
Allow Edit row actionviews/teams/components/Table.vueauth.can('teams-edit')
Allow Delete row actionviews/teams/components/Table.vueauth.can('teams-edit')
Fetch team listGET /teams (backend)teams-table permission via middleware
Create teamPOST /teams (backend)teams-add permission via middleware
Edit / delete teamPUT/DELETE /teams/{id} (backend)teams-edit permission via middleware
Fetch teams catalogGET /catalog/teams (backend)administrators-table permission via middleware

Envia Admin