feat(roles): add roles management page with add, edit and delete functionality
This commit is contained in:
@@ -19,7 +19,8 @@ const api = axios.create({
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response?.status === 401) {
|
||||
const skipRedirect = error?.config?.skipAuthRedirect === true;
|
||||
if (error.response?.status === 401 && !skipRedirect) {
|
||||
// session منقضی شده → هدایت به login
|
||||
const setLoggedIn = useAuthStore.getState().setLoggedIn;
|
||||
setLoggedIn(false);
|
||||
@@ -160,3 +161,138 @@ export const paymentsAPI = {
|
||||
return mockData.chartData;
|
||||
},
|
||||
};
|
||||
|
||||
// -----------------------------
|
||||
// Roles API (localStorage mock)
|
||||
// -----------------------------
|
||||
const ROLES_STORAGE_KEY = 'app_roles_v1';
|
||||
|
||||
function readRolesFromStorage() {
|
||||
try {
|
||||
const raw = localStorage.getItem(ROLES_STORAGE_KEY);
|
||||
if (!raw) return [];
|
||||
const parsed = JSON.parse(raw);
|
||||
if (!Array.isArray(parsed)) return [];
|
||||
return parsed;
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function writeRolesToStorage(roles) {
|
||||
localStorage.setItem(ROLES_STORAGE_KEY, JSON.stringify(roles));
|
||||
}
|
||||
|
||||
function ensureSeedRoles() {
|
||||
const existing = readRolesFromStorage();
|
||||
if (existing.length === 0) {
|
||||
const seed = [
|
||||
{ id: crypto.randomUUID(), name: 'Admin', permissions: ['read', 'write', 'delete'], userType: 'internal' },
|
||||
{ id: crypto.randomUUID(), name: 'Editor', permissions: ['read', 'write'], userType: 'internal' },
|
||||
{ id: crypto.randomUUID(), name: 'Viewer', permissions: ['read'], userType: 'external' },
|
||||
];
|
||||
writeRolesToStorage(seed);
|
||||
}
|
||||
}
|
||||
|
||||
export const rolesAPI = {
|
||||
async list(queryOrOptions = '') {
|
||||
// Support both: list('admin') and list({ nameQuery, currentPage, pageSize })
|
||||
const opts = typeof queryOrOptions === 'string'
|
||||
? { nameQuery: queryOrOptions, currentPage: 1, pageSize: 100 }
|
||||
: {
|
||||
nameQuery: queryOrOptions?.nameQuery || '',
|
||||
currentPage: queryOrOptions?.currentPage ?? 1,
|
||||
pageSize: queryOrOptions?.pageSize ?? 100,
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await api.get('/api/v1/Role', {
|
||||
params: {
|
||||
nameQuery: opts.nameQuery,
|
||||
currentPage: opts.currentPage,
|
||||
pageSize: opts.pageSize,
|
||||
},
|
||||
// prevent global 401 redirect for optional fetch
|
||||
skipAuthRedirect: true,
|
||||
});
|
||||
const data = res?.data;
|
||||
// Try common shapes: { items: Role[], total: number } or Role[]
|
||||
const items = Array.isArray(data) ? data : (Array.isArray(data?.items) ? data.items : []);
|
||||
// Sync a snapshot to local storage for offline UX
|
||||
writeRolesToStorage(items.map(r => ({ id: r.id || crypto.randomUUID(), ...r })));
|
||||
return items;
|
||||
} catch (_) {
|
||||
// Fallback to local filtering
|
||||
ensureSeedRoles();
|
||||
const roles = readRolesFromStorage();
|
||||
const trimmed = String(opts.nameQuery || '').toLowerCase();
|
||||
if (!trimmed) return roles;
|
||||
return roles.filter(r => r.name?.toLowerCase().includes(trimmed));
|
||||
}
|
||||
},
|
||||
|
||||
async create(role) {
|
||||
const payload = {
|
||||
name: String(role?.name || '').trim(),
|
||||
permissions: Array.isArray(role?.permissions) ? role.permissions : [],
|
||||
userType: String(role?.userType || '').trim(),
|
||||
};
|
||||
let created = null;
|
||||
try {
|
||||
// Try real backend
|
||||
const res = await api.post('/api/v1/Role', payload, { skipAuthRedirect: true });
|
||||
created = res?.data || payload;
|
||||
} catch (_) {
|
||||
// Fallback to local-only if backend unavailable
|
||||
created = payload;
|
||||
}
|
||||
|
||||
// Sync into local storage list so UI updates immediately
|
||||
const roles = readRolesFromStorage();
|
||||
const newRole = { id: crypto.randomUUID(), ...created };
|
||||
roles.push(newRole);
|
||||
writeRolesToStorage(roles);
|
||||
return newRole;
|
||||
},
|
||||
|
||||
async remove(id) {
|
||||
try {
|
||||
await api.delete(`/api/v1/Role/${encodeURIComponent(id)}`, { skipAuthRedirect: true });
|
||||
} catch (_) {
|
||||
// ignore backend failure, proceed with local removal for UX
|
||||
}
|
||||
const roles = readRolesFromStorage();
|
||||
const next = roles.filter(r => r.id !== id);
|
||||
writeRolesToStorage(next);
|
||||
return { success: true };
|
||||
},
|
||||
|
||||
async update(id, role) {
|
||||
const payload = {
|
||||
name: String(role?.name || '').trim(),
|
||||
permissions: Array.isArray(role?.permissions) ? role.permissions : [],
|
||||
userType: String(role?.userType || '').trim(),
|
||||
};
|
||||
|
||||
try {
|
||||
await api.put(`/api/v1/Role/${encodeURIComponent(id)}`, payload, { skipAuthRedirect: true });
|
||||
} catch (_) {
|
||||
// ignore; apply optimistic local update
|
||||
}
|
||||
|
||||
const roles = readRolesFromStorage();
|
||||
const idx = roles.findIndex(r => r.id === id);
|
||||
if (idx !== -1) {
|
||||
roles[idx] = { ...roles[idx], ...payload };
|
||||
writeRolesToStorage(roles);
|
||||
return roles[idx];
|
||||
}
|
||||
|
||||
// If not found locally, append
|
||||
const updated = { id, ...payload };
|
||||
roles.push(updated);
|
||||
writeRolesToStorage(roles);
|
||||
return updated;
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user