feat: replace mock system with real backend, RBAC, and Teams management
All checks were successful
Build and Deploy / build-and-push (push) Successful in 2m3s

- Implemented real JWT authentication and persistent user sessions
- Replaced all hardcoded mock data with dynamic MySQL-backed API calls
- Created new 'Times' (Teams) dashboard with performance metrics
- Renamed 'Equipe' to 'Membros' and centralized team management
- Added Role-Based Access Control (RBAC) for Admin/Manager/Agent roles
- Implemented secure invite-only member creation and password setup flow
- Enhanced Login with password visibility and real-time validation
- Added safe delete confirmation modal and custom Toast notifications
This commit is contained in:
Cauê Faleiros
2026-03-02 10:26:20 -03:00
parent 76b919d857
commit b7e73fce3d
19 changed files with 1707 additions and 553 deletions

View File

@@ -50,14 +50,19 @@ export const getUserById = async (id: string): Promise<User | undefined> => {
try {
const response = await fetch(`${API_URL}/users/${id}`);
if (!response.ok) return undefined;
return await response.json();
const contentType = response.headers.get("content-type");
if (contentType && contentType.indexOf("application/json") !== -1) {
return await response.json();
}
return undefined;
} catch (error) {
console.error("API Error (getUserById):", error);
return undefined;
}
};
export const updateUser = async (id: string, userData: { name: string, bio: string }): Promise<boolean> => {
export const updateUser = async (id: string, userData: any): Promise<boolean> => {
try {
const response = await fetch(`${API_URL}/users/${id}`, {
method: 'PUT',
@@ -71,11 +76,42 @@ export const updateUser = async (id: string, userData: { name: string, bio: stri
}
};
export const createMember = async (userData: any): Promise<boolean> => {
try {
const response = await fetch(`${API_URL}/users`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
return response.ok;
} catch (error) {
console.error("API Error (createMember):", error);
return false;
}
};
export const deleteUser = async (id: string): Promise<boolean> => {
try {
const response = await fetch(`${API_URL}/users/${id}`, {
method: 'DELETE'
});
return response.ok;
} catch (error) {
console.error("API Error (deleteUser):", error);
return false;
}
};
export const getAttendanceById = async (id: string): Promise<Attendance | undefined> => {
try {
const response = await fetch(`${API_URL}/attendances/${id}`);
if (!response.ok) return undefined;
return await response.json();
const contentType = response.headers.get("content-type");
if (contentType && contentType.indexOf("application/json") !== -1) {
return await response.json();
}
return undefined;
} catch (error) {
console.error("API Error (getAttendanceById):", error);
return undefined;
@@ -86,13 +122,57 @@ export const getTenants = async (): Promise<any[]> => {
try {
const response = await fetch(`${API_URL}/tenants`);
if (!response.ok) throw new Error('Falha ao buscar tenants');
return await response.json();
const contentType = response.headers.get("content-type");
if (contentType && contentType.indexOf("application/json") !== -1) {
return await response.json();
}
return [];
} catch (error) {
console.error("API Error (getTenants):", error);
return [];
}
};
export const getTeams = async (tenantId: string): Promise<any[]> => {
try {
const response = await fetch(`${API_URL}/teams?tenantId=${tenantId}`);
if (!response.ok) throw new Error('Falha ao buscar equipes');
return await response.json();
} catch (error) {
console.error("API Error (getTeams):", error);
return [];
}
};
export const createTeam = async (teamData: any): Promise<boolean> => {
try {
const response = await fetch(`${API_URL}/teams`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(teamData)
});
return response.ok;
} catch (error) {
console.error("API Error (createTeam):", error);
return false;
}
};
export const updateTeam = async (id: string, teamData: any): Promise<boolean> => {
try {
const response = await fetch(`${API_URL}/teams/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(teamData)
});
return response.ok;
} catch (error) {
console.error("API Error (updateTeam):", error);
return false;
}
};
export const createTenant = async (tenantData: any): Promise<boolean> => {
try {
const response = await fetch(`${API_URL}/tenants`, {
@@ -106,3 +186,87 @@ export const createTenant = async (tenantData: any): Promise<boolean> => {
return false;
}
};
// --- Auth Functions ---
export const login = async (credentials: any): Promise<any> => {
const response = await fetch(`${API_URL}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
});
const contentType = response.headers.get("content-type");
const isJson = contentType && contentType.indexOf("application/json") !== -1;
if (!response.ok) {
const error = isJson ? await response.json() : { error: 'Erro no servidor' };
throw new Error(error.error || 'Erro no login');
}
return isJson ? await response.json() : null;
};
export const register = async (userData: any): Promise<boolean> => {
const response = await fetch(`${API_URL}/auth/register`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Erro no registro');
}
return true;
};
export const verifyCode = async (data: any): Promise<boolean> => {
const response = await fetch(`${API_URL}/auth/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Código inválido ou expirado');
}
return true;
};
export const forgotPassword = async (email: string): Promise<string> => {
const response = await fetch(`${API_URL}/auth/forgot-password`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
const contentType = response.headers.get("content-type");
const isJson = contentType && contentType.indexOf("application/json") !== -1;
if (!response.ok) {
const error = isJson ? await response.json() : { error: 'Erro no servidor' };
throw new Error(error.error || 'Erro ao processar solicitação');
}
const data = isJson ? await response.json() : { message: 'Solicitação processada' };
return data.message;
};
export const resetPassword = async (password: string, token: string): Promise<string> => {
const response = await fetch(`${API_URL}/auth/reset-password`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ password, token })
});
const contentType = response.headers.get("content-type");
const isJson = contentType && contentType.indexOf("application/json") !== -1;
if (!response.ok) {
const error = isJson ? await response.json() : { error: 'Erro no servidor' };
throw new Error(error.error || 'Erro ao resetar senha');
}
const data = isJson ? await response.json() : { message: 'Senha redefinida' };
return data.message;
};