@snovasys/smart-libraries
v1.0.1
Published
Angular library for employee and team management with glassmorphism UI
Maintainers
Readme
Smart Libraries - Angular Component Library
Angular library for employee and team management with glassmorphism dark theme UI.
📦 Installation
npm install @snovasys/[email protected]🚀 Quick Start
import { OrganizationComponent } from '@snovasys/smart-libraries';
@Component({
selector: 'app-root',
standalone: true,
imports: [OrganizationComponent],
template: `
<sl-organization
[employees]="employees"
[teams]="teams"
[totalEmployees]="totalEmployees"
[totalTeams]="totalTeams"
[loading]="loading"
[employeesForDropdown]="employeesForDropdown"
[teamsForDropdown]="teamsForDropdown"
[rolesForDropdown]="rolesForDropdown"
[activeTab]="'employees'"
[activeViewMode]="'table'"
(tabChange)="onTabChange($event)"
(viewModeChange)="onViewModeChange($event)"
(employeeAction)="onEmployeeAction($event)"
(teamAction)="onTeamAction($event)"
(filterChange)="onFilterChange($event)">
</sl-organization>
`
})
export class AppComponent {
employees: Employee[] = [];
teams: Team[] = [];
// ... component implementation
}📚 Component Usage Options
Option 1: OrganizationComponent (Recommended) ✅
Use OrganizationComponent when you need both Employees and Teams functionality with tab navigation.
Benefits:
- One component handles everything
- Built-in tab navigation
- Consistent UI/UX
- Less code in your app
Example:
<sl-organization
[employees]="employees"
[teams]="teams"
[totalEmployees]="totalEmployees"
[totalTeams]="totalTeams"
[loading]="loading"
[employeesForDropdown]="employeesForDropdown"
[teamsForDropdown]="teamsForDropdown"
[rolesForDropdown]="rolesForDropdown"
[activeTab]="activeTab"
[activeViewMode]="activeViewMode"
(tabChange)="onTabChange($event)"
(viewModeChange)="onViewModeChange($event)"
(dropdownSearchChange)="onDropdownSearchChange($event)"
(employeeAction)="onEmployeeAction($event)"
(teamAction)="onTeamAction($event)"
(filterChange)="onFilterChange($event)">
</sl-organization>Option 2: Individual Components
Use individual components when you need only Employees or Teams functionality.
Employees Only:
<sl-employees
[employees]="employees"
[totalEmployees]="totalEmployees"
[loading]="loading"
[employeesForDropdown]="employeesForDropdown"
[teamsForDropdown]="teamsForDropdown"
[rolesForDropdown]="rolesForDropdown"
[activeViewMode]="activeViewMode"
(employeeAction)="onEmployeeAction($event)"
(filterChange)="onFilterChange($event)">
</sl-employees>Teams Only:
<sl-teams
[teams]="teams"
[totalTeams]="totalTeams"
[loading]="loading"
[employeesForDropdown]="employeesForDropdown"
[teamsForDropdown]="teamsForDropdown"
[rolesForDropdown]="rolesForDropdown"
[activeViewMode]="activeViewMode"
(dropdownSearchChange)="onDropdownSearchChange($event)"
(teamAction)="onTeamAction($event)"
(filterChange)="onFilterChange($event)">
</sl-teams>🎯 Input/Output Pattern
The library components use the Input/Output pattern - they accept data through @Input() and emit events through @Output(). This makes them decoupled from specific service implementations.
Pattern:
- Inputs: Pass data to components (employees, teams, loading state, etc.)
- Outputs: Handle events from components (CRUD operations, filter changes, etc.)
You control all data fetching and business logic in your parent component.
📖 Components
OrganizationComponent (sl-organization)
Combined view with tabs for employees and teams. Recommended for full-featured applications.
Inputs:
employees,teams,totalEmployees,totalTeams,loadingemployeesForDropdown,teamsForDropdown,rolesForDropdownactiveTab,activeViewMode
Outputs:
tabChange,viewModeChange,dropdownSearchChangeemployeeAction,teamAction,filterChange
EmployeesComponent (sl-employees)
Employee management with table and hierarchy views.
Inputs:
employees,totalEmployees,loadingemployeesForDropdown,teamsForDropdown,rolesForDropdownactiveViewMode
Outputs:
employeeAction,filterChange,dropdownSearchChange
TeamsComponent (sl-teams)
Team management with table and hierarchy views.
Inputs:
teams,totalTeams,loadingemployeesForDropdown,teamsForDropdown,rolesForDropdownactiveViewMode
Outputs:
teamAction,filterChange,dropdownSearchChange
🔧 Setup: Create Your API Service
You'll need to create your own API service to fetch data. The library components don't depend on any specific service implementation.
1. Create Your API Service
src/app/services/my-api.service.ts:
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import {
Employee,
CreateEmployee,
Team,
CreateTeam,
Role
} from '@snovasys/smart-libraries';
@Injectable({
providedIn: 'root'
})
export class MyApiService {
private apiUrl = 'https://your-api.com/api';
constructor(private http: HttpClient) {}
// Employee methods
getEmployeesPaginated(
searchText?: string,
status?: string,
page: number = 1,
pageSize: number = 50
): Observable<{ employees: Employee[]; totalCount: number }> {
let params = new HttpParams()
.set('page', page.toString())
.set('pageSize', pageSize.toString());
if (searchText) params = params.set('searchText', searchText);
if (status) params = params.set('status', status);
return this.http.get<{ employees: Employee[]; totalCount: number }>(
`${this.apiUrl}/employees`,
{ params }
);
}
createEmployee(employee: CreateEmployee): Observable<Employee> {
return this.http.post<Employee>(`${this.apiUrl}/employees`, employee);
}
updateEmployee(id: number, employee: Partial<CreateEmployee>): Observable<Employee> {
return this.http.put<Employee>(`${this.apiUrl}/employees/${id}`, employee);
}
// Team methods
getTeamsPaginated(
searchText?: string,
page: number = 1,
pageSize: number = 100
): Observable<{ teams: Team[]; totalCount: number }> {
let params = new HttpParams()
.set('page', page.toString())
.set('pageSize', pageSize.toString());
if (searchText) params = params.set('searchText', searchText);
return this.http.get<{ teams: Team[]; totalCount: number }>(
`${this.apiUrl}/teams`,
{ params }
);
}
createTeam(team: CreateTeam): Observable<Team> {
return this.http.post<Team>(`${this.apiUrl}/teams`, team);
}
// Dropdown data methods
getRoles(): Observable<Role[]> {
return this.http.get<Role[]>(`${this.apiUrl}/roles`);
}
searchEmployees(searchText: string): Observable<Employee[]> {
return this.http.get<Employee[]>(`${this.apiUrl}/employees/search`, {
params: { searchText }
});
}
searchTeams(searchText: string): Observable<Team[]> {
return this.http.get<Team[]>(`${this.apiUrl}/teams/search`, {
params: { searchText }
});
}
}2. Provide HttpClient
src/app/app.config.ts (Standalone):
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient()
]
};💡 Complete Example
src/app/pages/organization-page.component.ts:
import { Component, OnInit } from '@angular/core';
import {
OrganizationComponent,
Employee,
Team,
EmployeeActionEvent,
TeamActionEvent,
FilterChangeEvent
} from '@snovasys/smart-libraries';
import { MyApiService } from '../services/my-api.service';
@Component({
selector: 'app-organization-page',
standalone: true,
imports: [OrganizationComponent],
template: `
<sl-organization
[employees]="employees"
[teams]="teams"
[totalEmployees]="totalEmployees"
[totalTeams]="totalTeams"
[loading]="loading"
[employeesForDropdown]="employeesForDropdown"
[teamsForDropdown]="teamsForDropdown"
[rolesForDropdown]="rolesForDropdown"
[activeTab]="activeTab"
[activeViewMode]="activeViewMode"
(tabChange)="onTabChange($event)"
(viewModeChange)="onViewModeChange($event)"
(employeeAction)="onEmployeeAction($event)"
(teamAction)="onTeamAction($event)"
(filterChange)="onFilterChange($event)">
</sl-organization>
`
})
export class OrganizationPageComponent implements OnInit {
employees: Employee[] = [];
teams: Team[] = [];
employeesForDropdown: Employee[] = [];
teamsForDropdown: Team[] = [];
rolesForDropdown: Role[] = [];
totalEmployees = 0;
totalTeams = 0;
loading = false;
activeTab: 'employees' | 'teams' = 'employees';
activeViewMode: 'table' | 'hierarchy' = 'table';
constructor(private apiService: MyApiService) {}
ngOnInit() {
this.loadData();
}
loadData() {
this.loading = true;
// Load initial data based on active tab
if (this.activeTab === 'employees') {
this.loadEmployees();
} else {
this.loadTeams();
}
}
loadEmployees() {
this.apiService.getEmployeesPaginated().subscribe({
next: (response) => {
this.employees = response.employees;
this.totalEmployees = response.totalCount;
this.loading = false;
},
error: (err) => {
console.error('Error loading employees:', err);
this.loading = false;
}
});
}
loadTeams() {
this.apiService.getTeamsPaginated().subscribe({
next: (response) => {
this.teams = response.teams;
this.totalTeams = response.totalCount;
this.loading = false;
},
error: (err) => {
console.error('Error loading teams:', err);
this.loading = false;
}
});
}
onTabChange(event: { tab: 'employees' | 'teams' }) {
this.activeTab = event.tab;
this.loadData();
}
onViewModeChange(event: { mode: 'table' | 'hierarchy' }) {
this.activeViewMode = event.mode;
}
onEmployeeAction(event: EmployeeActionEvent) {
switch (event.action) {
case 'create':
this.apiService.createEmployee(event.data as CreateEmployee).subscribe({
next: () => this.loadEmployees(),
error: (err) => console.error('Error creating employee:', err)
});
break;
case 'update':
this.apiService.updateEmployee(event.data.id, event.data).subscribe({
next: () => this.loadEmployees(),
error: (err) => console.error('Error updating employee:', err)
});
break;
// Handle other actions...
}
}
onTeamAction(event: TeamActionEvent) {
switch (event.action) {
case 'create':
this.apiService.createTeam(event.data as CreateTeam).subscribe({
next: () => this.loadTeams(),
error: (err) => console.error('Error creating team:', err)
});
break;
// Handle other actions...
}
}
onFilterChange(event: FilterChangeEvent) {
// Reload data with new filters
if (this.activeTab === 'employees') {
this.apiService.getEmployeesPaginated(
event.filters.searchText,
event.filters.status,
event.pagination.page,
event.pagination.pageSize
).subscribe({
next: (response) => {
this.employees = response.employees;
this.totalEmployees = response.totalCount;
}
});
} else {
this.apiService.getTeamsPaginated(
event.filters.searchText,
event.pagination.page,
event.pagination.pageSize
).subscribe({
next: (response) => {
this.teams = response.teams;
this.totalTeams = response.totalCount;
}
});
}
}
}📋 Event Handling
OrganizationComponent Events
tabChange- When user switches between Employees/Teams tabsviewModeChange- When user toggles between Table/Hierarchy viewdropdownSearchChange- When dropdown search text changesemployeeAction- All employee-related CRUD operations (create, update, delete, toggleStatus, etc.)teamAction- All team-related CRUD operations (create, update, delete, addMember, etc.)filterChange- When filters or pagination change
Event Payload Examples
EmployeeActionEvent:
{
action: 'create' | 'update' | 'delete' | 'toggleStatus' | 'toggleLogin',
data: Employee | Partial<Employee>
}TeamActionEvent:
{
action: 'create' | 'update' | 'delete' | 'addMember' | 'removeMember' | 'addAdmin',
data: Team | Partial<Team> | any
}FilterChangeEvent:
{
filters: {
searchText?: string;
status?: string;
teamId?: number | null;
};
pagination: {
page: number;
pageSize: number;
totalCount: number;
};
}🔍 Troubleshooting
Components not rendering
- Check browser console for errors
- Verify all required inputs are provided
- Ensure HttpClient is provided (for API calls)
Type errors
- Ensure all types/interfaces are imported from
@snovasys/smart-libraries - Check that event handlers match the expected event types
Import errors
- Rebuild library:
ng build smart-libraries - Clear node_modules and reinstall:
rm -rf node_modules && npm install
Data not updating
- Ensure you're calling load methods after actions
- Check that event handlers are properly connected
- Verify API service methods are working correctly
📚 Additional Documentation
- USAGE_GUIDE.md - Complete detailed usage guide with more examples
- DATA_FORMAT_REFERENCE.md - Complete JSON format reference for all inputs and outputs
Access documentation:
- In
node_modules/@snovasys/smart-libraries/after installation - Or view on npm:
npm view @snovasys/smart-libraries
🔧 Development
Build Library
ng build smart-librariesTest Locally
# In library directory
cd dist/smart-libraries
npm link
# In consuming app
npm link @snovasys/smart-librariesPublishing
# Build the library
ng build smart-libraries
# Publish
cd dist/smart-libraries
npm publish --access public📝 Peer Dependencies
@angular/common: ^18.0.0@angular/core: ^18.0.0@angular/forms: ^18.0.0@angular/router: ^18.0.0rxjs: ^7.8.0 || ^8.0.0
📄 License
MIT
🔗 Further Help
To get more help on the Angular CLI use ng help or go check out the Angular CLI Overview and Command Reference page.
