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:
// 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:
| Field | Value |
|---|---|
action | teams |
lang | menu.teams |
is_menu | 1 |
parent_id | 0 (top-level) |
active | 1 |
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 requiredComponent Responsibilities
views/teams/index.vue
The page root. Responsibilities:
- Fetches
departmentsCatalog(GET /catalog/departments) andadministratorsCatalog(GET /catalog/administrators?active=true) on mount. provide()s both catalogs so child components caninject()them without prop drilling.- Renders
SectionTitlewith the Create button (visible only whenauth.can('teams-add')). - Hosts
TeamsTableandCreateTeamModalrefs. - Exposes
reloadTeamsData(): reloads the table and re-fetches catalogs, so that a newly created team leader appears in the dropdown immediately. - Handles the
@reload-catalogsevent emitted byTeamsTableand the@reload-tableevent emitted byCreateTeamModal.
views/teams/components/Table.vue
The paginated team list. Responsibilities:
- Calls
api.teams.getTeams(filters)through theDataTableloadDataprop. - Applies
normalizeActive(value)to every row'sactivefield before rendering, to handle MySQL'sBIT(1)→Bufferedge case:
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
activecolumn using theStatuscomponent (success/dangerbg-class). - Filters:
searchMultiple(id / name),select2for department and leader,selectfor status. - Row actions: Edit (opens
EditTeamModal) and Delete (Swal confirmation thenapi.teams.deleteTeam), both guarded byauth.can('teams-edit'). - Emits
reload-catalogsafter any mutation so the parent re-fetches the catalog used by the Administrators form.
views/teams/components/CreateTeam.modal.vue
Create panel. Responsibilities:
injectsdepartmentsCatalogandadministratorsCatalogfrom the parent.- Uses
Offcanvas+FormGeneratorwith fields:name,description,admin_id(required),department_id. - Calls
api.teams.createTeam(form.value.state)on save. - Emits
reload-tableon 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
activefield (select: Active / Inactive) — required. - Receives the team row via
show(item), which pre-populatesbasestate (includingNumber(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:
| Method | HTTP | Endpoint |
|---|---|---|
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:
inject('teamsCatalog', ref([]))to access the list.- Render a required
team_idselect field. - Use a
computed(selectedTeam) to find the full team object matching the currentform.state.team_id. watch(selectedTeam)to auto-fillform.state.department_idwithselectedTeam.department_idwhenever the user changes the team selection.- Set the
department_idfield asdisabled: !!selectedTeam.valueandrules: { 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
| Gate | Component | Check |
|---|---|---|
| Show Create button | views/teams/index.vue | auth.can('teams-add') |
| Allow Edit row action | views/teams/components/Table.vue | auth.can('teams-edit') |
| Allow Delete row action | views/teams/components/Table.vue | auth.can('teams-edit') |
| Fetch team list | GET /teams (backend) | teams-table permission via middleware |
| Create team | POST /teams (backend) | teams-add permission via middleware |
| Edit / delete team | PUT/DELETE /teams/{id} (backend) | teams-edit permission via middleware |
| Fetch teams catalog | GET /catalog/teams (backend) | administrators-table permission via middleware |
