@@ -443,19 +555,59 @@ const Location = () => {
{/* City Tab */}
{activeTab === 'city' && (
<>
-
-
-
-
setCityFilter(e.target.value)}
- placeholder="Filter cities..."
- className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500"
- />
+
+
+
+
+ setCityFilter(e.target.value)}
+ placeholder="Filter cities..."
+ className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500"
+ />
+
+
+
+
+
+
+
@@ -508,21 +660,27 @@ const Location = () => {
>
{currencies.map((curr) => (
-
))}
- setCountryForm({ ...countryForm, timeZoneName: e.target.value })}
className="w-full p-2 border rounded-lg"
- placeholder="e.g., UTC+3:30"
required
- />
+ >
+
+ {timeZones.map((tz) => (
+
+ ))}
+
- Khalij pay Admin Panel
+ Khalij Finance Admin
Sign in to your account
diff --git a/src/services/api.js b/src/services/api.js
index ab19331..8c257bb 100644
--- a/src/services/api.js
+++ b/src/services/api.js
@@ -14,3 +14,4 @@ export { provinceAPI } from './provinceAPI';
export { cityAPI } from './cityAPI';
export { issuerAPI } from './issuerAPI';
export { listPermissions } from './permissionsAPI';
+export { generalAPI } from './generalAPI';
diff --git a/src/services/apiClient.js b/src/services/apiClient.js
index 67b9081..ddd9408 100644
--- a/src/services/apiClient.js
+++ b/src/services/apiClient.js
@@ -43,15 +43,38 @@ if (typeof window !== 'undefined') {
// Request interceptor
// -----------------------------
api.interceptors.request.use(config => {
+ console.log("🔵 Request interceptor:", {
+ url: config.url,
+ fullUrl: config.baseURL + config.url,
+ skipAuthRedirect: config?.skipAuthRedirect,
+ method: config.method,
+ data: config.data,
+ headers: config.headers
+ });
+
const skipAuthRedirect = config?.skipAuthRedirect === true;
if (skipAuthRedirect) {
- const authState = useAuthStore.getState();
- if (!authState?.isLoggedIn) {
- const CancelToken = axios.CancelToken;
- const source = CancelToken.source();
- source.cancel('User not logged in');
- config.cancelToken = source.token;
- config._skipRequest = true;
+ // بررسی اینکه آیا درخواست به endpoint احراز هویت است یا نه
+ const isAuthEndpoint = config.url?.includes('/Auth/SignIn') ||
+ config.url?.includes('/Auth/SignOut') ||
+ config.url?.includes('/Auth/ForgotPassword');
+
+ console.log("🔵 Auth endpoint check:", { isAuthEndpoint, url: config.url });
+
+ // اگر endpoint احراز هویت است، اجازه ارسال درخواست را بدهیم (حتی اگر کاربر لاگین نباشد)
+ if (!isAuthEndpoint) {
+ const authState = useAuthStore.getState();
+ console.log("🔵 Auth state:", { isLoggedIn: authState?.isLoggedIn });
+ if (!authState?.isLoggedIn) {
+ console.warn("⚠️ Canceling request - user not logged in");
+ const CancelToken = axios.CancelToken;
+ const source = CancelToken.source();
+ source.cancel('User not logged in');
+ config.cancelToken = source.token;
+ config._skipRequest = true;
+ }
+ } else {
+ console.log("✅ Allowing auth endpoint request");
}
}
return config;
@@ -61,10 +84,59 @@ api.interceptors.request.use(config => {
// Response interceptor
// -----------------------------
api.interceptors.response.use(
- response => response,
+ response => {
+ console.log("🟢 Response interceptor - Success:", {
+ url: response.config?.url,
+ status: response.status,
+ data: response.data
+ });
+ return response;
+ },
error => {
+ console.log("🔴 Response interceptor - Error:", {
+ url: error.config?.url,
+ isSilent: error?.isSilent,
+ isCancel: axios.isCancel(error),
+ status: error?.response?.status,
+ message: error?.message
+ });
+
+ // لاگ کامل error.response
+ console.log("🔴 Full error.response:", error.response);
+ console.log("🔴 error.response exists:", !!error.response);
+
+ if (error.response) {
+ console.log("🔴 error.response.status:", error.response.status);
+ console.log("🔴 error.response.statusText:", error.response.statusText);
+ console.log("🔴 error.response.headers:", error.response.headers);
+ console.log("🔴 error.response.data exists:", !!error.response.data);
+ console.log("🔴 error.response.data:", error.response.data);
+ console.log("🔴 error.response.data type:", typeof error.response.data);
+
+ // بررسی اینکه آیا data یک string است
+ if (typeof error.response.data === 'string') {
+ console.log("🔴 Response data is string:", error.response.data);
+ }
+
+ // بررسی اینکه آیا data یک object است
+ if (error.response.data && typeof error.response.data === 'object') {
+ console.log("🔴 Response data keys:", Object.keys(error.response.data));
+ try {
+ console.log("🔴 Response data (JSON):", JSON.stringify(error.response.data, null, 2));
+ } catch (e) {
+ console.log("🔴 Cannot stringify response.data:", e);
+ }
+ }
+ } else {
+ console.log("🔴 No error.response object");
+ console.log("🔴 Full error object:", error);
+ }
+
if (error?.isSilent) return Promise.reject(error);
- if (axios.isCancel(error)) return Promise.reject({ isSilent: true, isCancel: true, response: { status: 401, data: { message: 'Unauthorized' } }, config: error.config || {} });
+ if (axios.isCancel(error)) {
+ console.warn("⚠️ Request was canceled");
+ return Promise.reject({ isSilent: true, isCancel: true, response: { status: 401, data: { message: 'Unauthorized' } }, config: error.config || {} });
+ }
const skipRedirect = error?.config?.skipAuthRedirect === true;
const status = error?.response?.status;
@@ -84,10 +156,45 @@ api.interceptors.response.use(
}
// حفظ status code در خطا برای مدیریت بهتر
- const errorData = error.response?.data || error;
+ let errorData = error.response?.data;
+
+ // اگر errorData خودش یک AxiosError باشد، پیام خطا را از آن استخراج کنیم
+ if (errorData && typeof errorData === 'object' && errorData.name === 'AxiosError') {
+ errorData = {
+ message: errorData.message || error.message || 'خطا در ارتباط با سرور',
+ ...(errorData.response?.data || {})
+ };
+ }
+
+ // اگر errorData وجود نداشت یا خالی است، از status code برای ایجاد پیام مناسب استفاده کنیم
+ if (!errorData || (typeof errorData === 'object' && Object.keys(errorData).length === 0)) {
+ const status = error.response?.status;
+ let message = error.message || 'خطا در ارتباط با سرور';
+
+ // پیامهای مناسب بر اساس status code
+ if (status === 500) {
+ message = 'خطای سرور. لطفاً با پشتیبانی تماس بگیرید یا دوباره تلاش کنید.';
+ } else if (status === 400) {
+ message = 'درخواست نامعتبر. لطفاً اطلاعات را بررسی کنید.';
+ } else if (status === 401) {
+ message = 'نام کاربری یا رمز عبور اشتباه است.';
+ } else if (status === 403) {
+ message = 'شما دسترسی به این بخش را ندارید.';
+ } else if (status === 404) {
+ message = 'منبع مورد نظر یافت نشد.';
+ } else if (status === 502 || status === 503) {
+ message = 'سرور در دسترس نیست. لطفاً بعداً تلاش کنید.';
+ }
+
+ errorData = {
+ message: message,
+ statusCode: status
+ };
+ }
+
if (error.response) {
return Promise.reject({
- ...errorData,
+ ...error,
response: {
...error.response,
status: error.response.status,
@@ -96,7 +203,7 @@ api.interceptors.response.use(
});
}
- return Promise.reject(errorData);
+ return Promise.reject(error);
}
);
diff --git a/src/services/authAPI.js b/src/services/authAPI.js
index b411e73..b7dbe51 100644
--- a/src/services/authAPI.js
+++ b/src/services/authAPI.js
@@ -4,9 +4,42 @@ import { useAuthStore } from "../store/authStore";
// -----------------------------
// Auth API
// -----------------------------
-export async function login(username, password) {
- const res = await api.post("/api/v1/Auth/SignIn", { userName: username, username, email: username, password });
- return res.data;
+export async function login(email, password) {
+ try {
+ const res = await api.post("/api/v1/Auth/SignIn", { email: email, password }, { skipAuthRedirect: true });
+ const data = res.data;
+
+ console.log("data login", data);
+
+ // بررسی اینکه آیا پاسخ موفقیتآمیز است یا نه (پشتیبانی از isSuccess و IsSuccess)
+ if (data && (data.isSuccess === false || data.IsSuccess === false)) {
+ // ایجاد یک خطا با پیام دریافتی از API (پشتیبانی از message و Message)
+ const errorMessage = data.message || data.Message || 'Login failed';
+ const error = new Error(errorMessage);
+ error.response = {
+ ...res,
+ data: data,
+ status: data.statusCode || data.StatusCode || 409
+ };
+ throw error;
+ }
+
+ return data;
+ } catch (error) {
+
+
+ // اگر خطا از سمت سرور است (500, 400, etc.)، آن را throw کنیم
+ if (error.response) {
+ const errorData = error.response.data || {};
+ const errorMessage = errorData.message || errorData.Message || error.message || 'خطا در ارتباط با سرور';
+ const customError = new Error(errorMessage);
+ customError.response = error.response;
+ throw customError;
+ }
+
+ // برای خطاهای دیگر (network error, etc.)
+ throw error;
+ }
}
export async function signOut() {
@@ -15,7 +48,8 @@ export async function signOut() {
} catch (error) {
console.warn("SignOut API error:", error);
}
- useAuthStore.getState().setLoggedIn(false);
+ const { setLoggedIn, logout } = useAuthStore.getState();
+ logout(); // This will clear both isLoggedIn and user
window.location.href = "/";
}
diff --git a/src/services/cityAPI.js b/src/services/cityAPI.js
index 54f96ed..bff8ede 100644
--- a/src/services/cityAPI.js
+++ b/src/services/cityAPI.js
@@ -4,13 +4,29 @@ import api from './apiClient';
// City API
// -----------------------------
export const cityAPI = {
- // GET /api/v1/City (with pagination)
+ // GET /api/v1/City (with pagination and filters)
async list(params = {}) {
- const { currentPage = 1, pageSize = 10, ...otherParams } = params;
+ const { currentPage = 1, pageSize = 10, countryId, provinceId, ...otherParams } = params;
+ const requestParams = { currentPage, pageSize, ...otherParams };
+
+ // اگر countryId یا provinceId وجود دارد، آنها را به params اضافه کن
+ if (countryId) {
+ requestParams.countryId = countryId;
+ }
+ if (provinceId) {
+ requestParams.provinceId = provinceId;
+ }
+
+ console.log('🔵 City API - list request:', { params: requestParams });
+
const res = await api.get('/api/v1/City', {
- params: { currentPage, pageSize, ...otherParams },
+ params: requestParams,
skipAuthRedirect: true
});
+
+ console.log('🟢 City API - list response:', res?.data);
+
+ // پاسخ به صورت { data: { data: [...], filterSummary: {...} } } است
return res?.data?.data?.data || [];
},
diff --git a/src/services/countryAPI.js b/src/services/countryAPI.js
index 60f1b2c..1902192 100644
--- a/src/services/countryAPI.js
+++ b/src/services/countryAPI.js
@@ -16,8 +16,15 @@ export const countryAPI = {
// GET /api/v1/Country/All (all countries without pagination)
async listAll() {
- const res = await api.get('/api/v1/Country/All', { skipAuthRedirect: true });
- return res?.data?.data || [];
+ try {
+ const res = await api.get('/api/v1/Country/All', { skipAuthRedirect: true });
+ // پاسخ به صورت { data: [...], statusCode: 200, isSuccess: true, ... } است
+ console.log('🔵 Country API - listAll response:', res?.data);
+ return res?.data?.data || [];
+ } catch (error) {
+ console.error('🔴 Country API - listAll error:', error);
+ throw error;
+ }
},
// POST /api/v1/Country
@@ -26,8 +33,9 @@ export const countryAPI = {
Name: String(country?.name || ''),
PhoneCode: String(country?.phoneCode || ''),
CurrencyCode: String(country?.currencyCode || ''),
- TimeZoneName: String(country?.timeZoneName || country?.timeZone || ''),
+ TimeZone: String(country?.timeZoneName || country?.timeZone || ''),
};
+ console.log('🔵 Country API - create payload:', payload);
const res = await api.post('/api/v1/Country', payload, { skipAuthRedirect: true });
return res?.data;
},
@@ -37,7 +45,7 @@ export const countryAPI = {
if (!countryId) {
throw new Error('Country ID is required');
}
- // ساخت payload - سرور انتظار فیلدها با حرف بزرگ دارد
+ // ساخت payload - سرور در PUT انتظار TimeZoneName دارد (نه TimeZone)
const payload = {
Name: country?.name ? String(country.name).trim() : '',
PhoneCode: country?.phoneCode ? String(country.phoneCode).trim() : '',
@@ -46,13 +54,13 @@ export const countryAPI = {
};
const url = `/api/v1/Country/${encodeURIComponent(countryId)}`;
- console.log('Country API Update:', { url, countryId, payload });
+ console.log('🔵 Country API - update payload:', { url, countryId, payload });
try {
const res = await api.put(url, payload, { skipAuthRedirect: true });
return res?.data;
} catch (error) {
- console.error('Country API Update Error:', {
+ console.error('🔴 Country API - update error:', {
url,
countryId,
payload,
diff --git a/src/services/generalAPI.js b/src/services/generalAPI.js
new file mode 100644
index 0000000..ecf60a3
--- /dev/null
+++ b/src/services/generalAPI.js
@@ -0,0 +1,43 @@
+import api from './apiClient';
+
+// -----------------------------
+// General API
+// -----------------------------
+export const generalAPI = {
+ // GET /api/v1/General/Currencies
+ async getCurrencies() {
+ try {
+ const res = await api.get('/api/v1/General/Currencies', { skipAuthRedirect: true });
+ // پاسخ به صورت array مستقیم است
+ return Array.isArray(res?.data) ? res?.data : [];
+ } catch (error) {
+ console.error('🔴 General API - getCurrencies error:', error);
+ throw error;
+ }
+ },
+
+ // GET /api/v1/General/TimeZones
+ async getTimeZones() {
+ try {
+ const res = await api.get('/api/v1/General/TimeZones', { skipAuthRedirect: true });
+ // پاسخ به صورت { data: [...], statusCode: 200, ... } است
+ return res?.data?.data || [];
+ } catch (error) {
+ console.error('🔴 General API - getTimeZones error:', error);
+ throw error;
+ }
+ },
+
+ // GET /api/v1/General/IssuerCapabilities
+ async getIssuerCapabilities() {
+ try {
+ const res = await api.get('/api/v1/General/IssuerCapabilities', { skipAuthRedirect: true });
+ // پاسخ به صورت { data: [...], statusCode: 200, ... } است
+ return res?.data?.data || [];
+ } catch (error) {
+ console.error('🔴 General API - getIssuerCapabilities error:', error);
+ throw error;
+ }
+ },
+};
+
diff --git a/src/services/issuerAPI.js b/src/services/issuerAPI.js
index 19ca9cd..cf1f279 100644
--- a/src/services/issuerAPI.js
+++ b/src/services/issuerAPI.js
@@ -4,13 +4,32 @@ import api from './apiClient';
// Issuer API
// -----------------------------
export const issuerAPI = {
- // GET /api/v1/Issuer (with pagination)
+ // GET /api/v1/Issuer (with pagination and filters)
async list(params = {}) {
- const { currentPage = 1, pageSize = 100, ...otherParams } = params;
+ const { currentPage = 1, pageSize = 100, isActive, supportCurrencyCode, supportCapabilities, ...otherParams } = params;
+ const requestParams = { currentPage, pageSize, ...otherParams };
+
+ // اگر فیلترها وجود دارند، آنها را به params اضافه کن
+ if (isActive !== undefined && isActive !== null && isActive !== '') {
+ requestParams.isActive = isActive;
+ }
+ if (supportCurrencyCode) {
+ requestParams.supportCurrencyCode = supportCurrencyCode;
+ }
+ if (supportCapabilities) {
+ requestParams.supportCapabilities = supportCapabilities;
+ }
+
+ console.log('🔵 Issuer API - list request:', { params: requestParams });
+
const res = await api.get('/api/v1/Issuer', {
- params: { currentPage, pageSize, ...otherParams },
+ params: requestParams,
skipAuthRedirect: true
});
+
+ console.log('🟢 Issuer API - list response:', res?.data);
+
+ // پاسخ به صورت { data: { data: [...], filterSummary: {...} } } است
return res?.data?.data?.data || [];
},
@@ -26,46 +45,117 @@ export const issuerAPI = {
// POST /api/v1/Issuer
async create(issuer) {
+ // Build payload according to AddIssuerCommand structure:
+ // name (required), supportEmail (required), title (nullable), cityId (Guid nullable), postalCode, addressDetails (nullable)
const payload = {
- name: String(issuer?.name || ''),
- supportEmail: String(issuer?.supportEmail || ''),
- postalCode: String(issuer?.postalCode || ''),
- addressDetails: String(issuer?.addressDetails || '')
+ name: String(issuer?.name || '').trim(),
+ supportEmail: String(issuer?.supportEmail || '').trim(),
+ postalCode: String(issuer?.postalCode || '').trim()
};
- // Include cityId - use null if empty or invalid
- if (issuer?.cityId && issuer.cityId !== '' && issuer.cityId !== 'null' && issuer.cityId !== null) {
- payload.cityId = issuer.cityId;
- } else {
- payload.cityId = null;
+ // Add title only if provided (nullable field - don't send if empty)
+ if (issuer?.title && String(issuer.title).trim()) {
+ payload.title = String(issuer.title).trim();
}
- console.log('Issuer Create Payload:', payload);
- const res = await api.post('/api/v1/Issuer', payload, { skipAuthRedirect: true });
- return res?.data;
+ // Add addressDetails only if provided (nullable field - don't send if empty)
+ const addressValue = String(issuer?.addressDetails || issuer?.address || '').trim();
+ if (addressValue) {
+ payload.addressDetails = addressValue;
+ }
+
+ // Include cityId only if valid GUID (nullable Guid - don't send if null/empty)
+ if (issuer?.cityId && issuer.cityId !== '' && issuer.cityId !== 'null' && issuer.cityId !== null) {
+ // Validate GUID format (basic check)
+ const guidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
+ if (guidPattern.test(issuer.cityId)) {
+ payload.cityId = issuer.cityId;
+ } else {
+ console.warn('⚠️ Invalid cityId GUID format:', issuer.cityId);
+ }
+ }
+
+ console.log('🔵 Issuer Create Payload:', JSON.stringify(payload, null, 2));
+ console.log('🔵 Issuer Create Payload (original):', issuer);
+
+ try {
+ const res = await api.post('/api/v1/Issuer', payload, { skipAuthRedirect: true });
+ console.log('🟢 Issuer Create Response:', res?.data);
+ return res?.data;
+ } catch (err) {
+ console.error('🔴 Issuer Create Error:', {
+ status: err?.response?.status,
+ statusText: err?.response?.statusText,
+ data: err?.response?.data,
+ errors: err?.response?.data?.errors,
+ message: err?.response?.data?.message,
+ payload: payload
+ });
+ console.error('🔴 Full error.response:', err?.response);
+ console.error('🔴 Full error.response.data:', err?.response?.data);
+ console.error('🔴 Error response data type:', typeof err?.response?.data);
+ if (err?.response?.data) {
+ try {
+ console.error('🔴 Error response data stringified:', JSON.stringify(err?.response?.data, null, 2));
+ } catch (e) {
+ console.error('🔴 Error response data (could not stringify):', err?.response?.data);
+ console.error('🔴 Error response data toString:', String(err?.response?.data));
+ }
+ }
+ throw err;
+ }
},
// PUT /api/v1/Issuer/{id}
async update(id, issuer) {
+ // Build payload according to UpdateIssuerCommand structure (similar to AddIssuerCommand)
const payload = {
- name: String(issuer?.name || ''),
- supportEmail: String(issuer?.supportEmail || ''),
- postalCode: String(issuer?.postalCode || ''),
- addressDetails: String(issuer?.addressDetails || '')
+ name: String(issuer?.name || '').trim(),
+ supportEmail: String(issuer?.supportEmail || '').trim(),
+ postalCode: String(issuer?.postalCode || '').trim()
};
- // Include cityId - use null if empty or invalid
- if (issuer?.cityId && issuer.cityId !== '' && issuer.cityId !== 'null' && issuer.cityId !== null) {
- payload.cityId = issuer.cityId;
- } else {
- payload.cityId = null;
+ // Add title if provided (nullable field)
+ if (issuer?.title && String(issuer.title).trim()) {
+ payload.title = String(issuer.title).trim();
}
- console.log('Issuer Update Payload:', payload);
- const res = await api.put(`/api/v1/Issuer/${encodeURIComponent(id)}`, payload, {
- skipAuthRedirect: true
- });
- return res?.data;
+ // Add addressDetails if provided (nullable field)
+ const addressValue = String(issuer?.addressDetails || issuer?.address || '').trim();
+ if (addressValue) {
+ payload.addressDetails = addressValue;
+ }
+
+ // Include cityId - validate GUID format
+ if (issuer?.cityId && issuer.cityId !== '' && issuer.cityId !== 'null' && issuer.cityId !== null) {
+ // Validate GUID format (basic check)
+ const guidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
+ if (guidPattern.test(issuer.cityId)) {
+ payload.cityId = issuer.cityId;
+ } else {
+ console.warn('⚠️ Invalid cityId GUID format:', issuer.cityId);
+ }
+ }
+
+ console.log('🔵 Issuer Update Payload:', JSON.stringify(payload, null, 2));
+
+ try {
+ const res = await api.put(`/api/v1/Issuer/${encodeURIComponent(id)}`, payload, {
+ skipAuthRedirect: true
+ });
+ console.log('🟢 Issuer Update Response:', res?.data);
+ return res?.data;
+ } catch (err) {
+ console.error('🔴 Issuer Update Error:', {
+ status: err?.response?.status,
+ statusText: err?.response?.statusText,
+ data: err?.response?.data,
+ errors: err?.response?.data?.errors,
+ message: err?.response?.data?.message,
+ payload: payload
+ });
+ throw err;
+ }
},
// DELETE /api/v1/Issuer/{id}
@@ -84,111 +174,201 @@ export const issuerAPI = {
return res?.data;
},
- // GET /api/v1/Issuer/{id}/Capabilities
- async getCapabilities(id) {
+ // GET /api/v1/Issuer/{issuerId}/Capabilities
+ async getCapabilities(issuerId) {
try {
- const res = await api.get(`/api/v1/Issuer/${encodeURIComponent(id)}/Capabilities`, {
+ console.log('🔵 Getting capabilities for issuer:', issuerId);
+ const res = await api.get(`/api/v1/Issuer/${encodeURIComponent(issuerId)}/Capabilities`, {
skipAuthRedirect: true
});
// Response structure: { data: [...], statusCode, isSuccess, ... }
- const capabilities = res?.data?.data || [];
- console.log('Capabilities from API:', capabilities);
+ // Or direct array: [...]
+ const capabilities = Array.isArray(res?.data) ? res.data : (res?.data?.data || []);
+ console.log('🟢 Capabilities from API:', capabilities);
return capabilities;
} catch (err) {
+ console.error('🔴 Error getting capabilities:', {
+ status: err?.response?.status,
+ data: err?.response?.data
+ });
// Handle 404 gracefully - endpoint might not exist for some issuers
if (err?.response?.status === 404) {
- console.warn('Capabilities endpoint not found (404), returning empty array');
+ console.warn('⚠️ Capabilities endpoint not found (404), returning empty array');
return [];
}
throw err;
}
},
- // PUT /api/v1/Issuer/{id}/Capabilities
- async updateCapabilities(id, capabilities) {
- // Filter out invalid capabilities and ensure all required fields are present
+ // PUT /api/v1/Issuer/{issuerId}/Capabilities
+ // Payload: IssuerManageCapabilitiesCommand { capabilities: List }
+ async updateCapabilities(issuerId, capabilities) {
+ // Build IssuerCapabilityDto array from input
+ // Each IssuerCapabilityDto should have: capability, capabilityName, hasCapability (or similar fields)
const validCapabilities = Array.isArray(capabilities) ? capabilities
.filter(cap => {
- const capabilityValue = typeof cap === 'string' ? cap : (cap.capability || '');
- return capabilityValue && String(capabilityValue).trim() !== '';
+ // Filter out invalid capabilities
+ if (typeof cap === 'string') {
+ return cap && String(cap).trim() !== '';
+ }
+ if (typeof cap === 'object' && cap !== null) {
+ const capabilityValue = cap.capability || cap.capabilityName || '';
+ return capabilityValue && String(capabilityValue).trim() !== '';
+ }
+ return false;
})
.map(cap => {
- const capabilityValue = typeof cap === 'string' ? cap : (cap.capability || '');
- const capabilityName = typeof cap === 'object' && cap.capabilityName ? cap.capabilityName : capabilityValue;
- const hasCapability = typeof cap === 'object' ? (cap.hasCapability !== undefined ? cap.hasCapability : true) : true;
-
- // Ensure capability is a valid string (not empty, not null, not undefined)
- const capValue = String(capabilityValue).trim();
- if (!capValue) {
- return null; // Will be filtered out
+ // Convert to IssuerCapabilityDto format
+ if (typeof cap === 'string') {
+ return {
+ capability: String(cap).trim(),
+ capabilityName: String(cap).trim(),
+ hasCapability: true
+ };
}
+ // If it's an object, ensure it has the required fields
+ const capabilityValue = cap.capability || cap.capabilityName || '';
+ const capabilityName = cap.capabilityName || cap.capability || String(capabilityValue).trim();
+ const hasCapability = cap.hasCapability !== undefined ? Boolean(cap.hasCapability) : true;
+
return {
- capability: capValue,
- capabilityName: String(capabilityName).trim() || capValue,
- hasCapability: Boolean(hasCapability)
+ capability: String(capabilityValue).trim(),
+ capabilityName: String(capabilityName).trim() || String(capabilityValue).trim(),
+ hasCapability: hasCapability
};
})
- .filter(cap => cap !== null) : [];
+ .filter(cap => cap.capability && cap.capability.trim() !== '') : [];
// Wrap in IssuerManageCapabilitiesCommand structure
const payload = {
capabilities: validCapabilities
};
- console.log('Update Capabilities Payload:', JSON.stringify(payload, null, 2));
- console.log('Valid capabilities count:', validCapabilities.length);
- console.log('Capabilities details:', validCapabilities);
+ console.log('🔵 Update Capabilities Payload:', JSON.stringify(payload, null, 2));
+ console.log('🔵 Issuer ID:', issuerId);
+ console.log('🔵 Valid capabilities count:', validCapabilities.length);
try {
- const res = await api.put(`/api/v1/Issuer/${encodeURIComponent(id)}/Capabilities`, payload, {
+ const res = await api.put(`/api/v1/Issuer/${encodeURIComponent(issuerId)}/Capabilities`, payload, {
skipAuthRedirect: true
});
+ console.log('🟢 Capabilities Update Response:', res?.data);
return res?.data;
} catch (err) {
- console.error('Capabilities update error details:', {
+ console.error('🔴 Capabilities update error:', {
status: err?.response?.status,
+ statusText: err?.response?.statusText,
data: err?.response?.data,
errors: err?.response?.data?.errors,
+ message: err?.response?.data?.message,
payload: payload
});
+ if (err?.response?.data) {
+ try {
+ console.error('🔴 Error response data stringified:', JSON.stringify(err?.response?.data, null, 2));
+ } catch (e) {
+ console.error('🔴 Error response data (could not stringify):', err?.response?.data);
+ }
+ }
throw err;
}
},
- // GET /api/v1/Issuer/{id}/AllowedCurrencies
- async getAllowedCurrencies(id) {
+ // GET /api/v1/Issuer/{issuerId}/AllowedCurrencies
+ async getAllowedCurrencies(issuerId) {
try {
- const res = await api.get(`/api/v1/Issuer/${encodeURIComponent(id)}/AllowedCurrencies`, {
+ console.log('🔵 Getting allowed currencies for issuer:', issuerId);
+ const res = await api.get(`/api/v1/Issuer/${encodeURIComponent(issuerId)}/AllowedCurrencies`, {
skipAuthRedirect: true
});
- // Response is a direct array: [{code, name, nativeName, symbol, numericCode, decimalPlaces}, ...]
+ // Response is a direct array: [{code, name, nativeName, symbol, numericCode, decimalPlaces, allowed}, ...]
+ // Or wrapped: { data: [...] }
const currencies = Array.isArray(res?.data) ? res.data : (res?.data?.data || []);
- console.log('Allowed Currencies from API:', currencies);
+ console.log('🟢 Allowed Currencies from API:', currencies);
return currencies;
} catch (err) {
+ console.error('🔴 Error getting allowed currencies:', {
+ status: err?.response?.status,
+ data: err?.response?.data
+ });
// Handle 404 gracefully - endpoint might not exist for some issuers
if (err?.response?.status === 404) {
- console.warn('AllowedCurrencies endpoint not found (404), returning empty array');
+ console.warn('⚠️ AllowedCurrencies endpoint not found (404), returning empty array');
return [];
}
throw err;
}
},
- // PUT /api/v1/Issuer/{id}/AllowedCurrencies
- async updateAllowedCurrencies(id, allowedCurrencies) {
+ // PUT /api/v1/Issuer/{issuerId}/AllowedCurrencies
+ // Payload: { allowedCurrencies: List }
+ async updateAllowedCurrencies(issuerId, allowedCurrencies) {
+ // Build IssuerAllowedCurrencyDto array from input
+ // Each should have: currencyCode, currencyEnglishName, allowed
+ const currencyMap = new Map(); // Use Map to avoid duplicates
+
+ if (Array.isArray(allowedCurrencies)) {
+ allowedCurrencies.forEach(curr => {
+ const currencyCode = String(curr.currencyCode || curr.code || '').trim();
+ if (!currencyCode) return; // Skip invalid currencies
+
+ // Use currencyCode as key to avoid duplicates
+ if (!currencyMap.has(currencyCode)) {
+ const currencyEnglishName = String(curr.currencyEnglishName || curr.name || '').trim();
+ const allowed = curr.allowed !== undefined ? Boolean(curr.allowed) : true;
+
+ currencyMap.set(currencyCode, {
+ currencyCode: currencyCode,
+ currencyEnglishName: currencyEnglishName || currencyCode,
+ allowed: allowed
+ });
+ }
+ });
+ }
+
+ // Convert Map to array
+ const validCurrencies = Array.from(currencyMap.values());
+
+ // Wrap in command structure
const payload = {
- allowedCurrencies: Array.isArray(allowedCurrencies) ? allowedCurrencies.map(curr => ({
- currencyCode: curr.currencyCode || curr.code || '',
- currencyEnglishName: curr.currencyEnglishName || curr.name || '',
- allowed: curr.allowed !== undefined ? curr.allowed : true
- })) : []
+ allowedCurrencies: validCurrencies
};
- const res = await api.put(`/api/v1/Issuer/${encodeURIComponent(id)}/AllowedCurrencies`, payload, {
- skipAuthRedirect: true
- });
- return res?.data;
+
+ console.log('🔵 Update Allowed Currencies Payload:', JSON.stringify(payload, null, 2));
+ console.log('🔵 Issuer ID:', issuerId);
+ console.log('🔵 Valid currencies count:', validCurrencies.length);
+ console.log('🔵 Currencies with allowed=true:', validCurrencies.filter(c => c.allowed).length);
+ console.log('🔵 Currencies with allowed=false:', validCurrencies.filter(c => !c.allowed).length);
+ console.log('🔵 Duplicate check - unique currency codes:', [...currencyMap.keys()]);
+
+ try {
+ const res = await api.put(`/api/v1/Issuer/${encodeURIComponent(issuerId)}/AllowedCurrencies`, payload, {
+ skipAuthRedirect: true
+ });
+ console.log('🟢 Allowed Currencies Update Response:', res?.data);
+ return res?.data;
+ } catch (err) {
+ console.error('🔴 Allowed Currencies update error:', {
+ status: err?.response?.status,
+ statusText: err?.response?.statusText,
+ data: err?.response?.data,
+ errors: err?.response?.data?.errors,
+ message: err?.response?.data?.message,
+ code: err?.response?.data?.code,
+ payload: payload
+ });
+ console.error('🔴 Full error.response:', err?.response);
+ if (err?.response?.data) {
+ try {
+ console.error('🔴 Error response data stringified:', JSON.stringify(err?.response?.data, null, 2));
+ } catch (e) {
+ console.error('🔴 Error response data (could not stringify):', err?.response?.data);
+ console.error('🔴 Error response data toString:', String(err?.response?.data));
+ }
+ }
+ throw err;
+ }
}
};
diff --git a/src/services/provinceAPI.js b/src/services/provinceAPI.js
index 6ba4513..a0b48f7 100644
--- a/src/services/provinceAPI.js
+++ b/src/services/provinceAPI.js
@@ -4,13 +4,26 @@ import api from './apiClient';
// Province API
// -----------------------------
export const provinceAPI = {
- // GET /api/v1/Province (with pagination)
+ // GET /api/v1/Province (with pagination and filters)
async list(params = {}) {
- const { currentPage = 1, pageSize = 10, ...otherParams } = params;
+ const { currentPage = 1, pageSize = 10, countryId, ...otherParams } = params;
+ const requestParams = { currentPage, pageSize, ...otherParams };
+
+ // اگر countryId وجود دارد، آن را به params اضافه کن
+ if (countryId) {
+ requestParams.countryId = countryId;
+ }
+
+ console.log('🔵 Province API - list request:', { params: requestParams });
+
const res = await api.get('/api/v1/Province', {
- params: { currentPage, pageSize, ...otherParams },
+ params: requestParams,
skipAuthRedirect: true
});
+
+ console.log('🟢 Province API - list response:', res?.data);
+
+ // پاسخ به صورت { data: { data: [...], filterSummary: {...} } } است
return res?.data?.data?.data || [];
},
diff --git a/src/services/usersAPI.js b/src/services/usersAPI.js
index dd3bc1a..6e4abb0 100644
--- a/src/services/usersAPI.js
+++ b/src/services/usersAPI.js
@@ -9,8 +9,44 @@ function writeUsersToStorage(users){ localStorage.setItem(USERS_STORAGE_KEY, JSO
export const usersAPI = {
async list({searchQuery='',currentPage=1,pageSize=100}={}) {
- const res = await api.get('/api/v1/User',{ params:{searchQuery,currentPage,pageSize}, skipAuthRedirect:true });
- return res?.data?.data?.data||[];
+ try {
+ // اطمینان از اینکه params همیشه object است
+ const params = {
+ searchQuery: searchQuery || '',
+ currentPage: currentPage || 1,
+ pageSize: pageSize || 100
+ };
+
+ console.log('🔵 Users API - list request:', {
+ url: '/api/v1/User',
+ method: 'GET',
+ params
+ });
+
+ const res = await api.get('/api/v1/User',{
+ params,
+ skipAuthRedirect:true
+ });
+
+ console.log('🟢 Users API - list response:', res?.data);
+
+ return res?.data?.data?.data||[];
+ } catch (error) {
+ console.error('🔴 Users API - list error:', error);
+ console.error('🔴 Error details:', {
+ url: error.config?.url,
+ method: error.config?.method,
+ params: error.config?.params,
+ data: error.config?.data,
+ response: error.response?.data
+ });
+ throw error;
+ }
+ },
+ async get(id){
+ if (!id) throw new Error('User ID is required');
+ const res = await api.get(`/api/v1/User/${encodeURIComponent(id)}`, { skipAuthRedirect: true });
+ return res?.data?.data || res?.data;
},
async create(user){
const payload = { firstName:String(user?.firstName||''), lastName:String(user?.lastName||''), email:String(user?.email||''), mobile:String(user?.mobile||''), isActive:!!user?.isActive };
diff --git a/src/store/authStore.js b/src/store/authStore.js
index f5d4a1d..0d60c2c 100644
--- a/src/store/authStore.js
+++ b/src/store/authStore.js
@@ -6,12 +6,15 @@ export const useAuthStore = create(
(set) => ({
isLoggedIn: false,
loading: false,
+ user: null,
setLoggedIn: (status) => set({ isLoggedIn: status }),
setLoading: (status) => set({ loading: status }),
+ setUser: (userData) => set({ user: userData }),
+ logout: () => set({ isLoggedIn: false, user: null }),
}),
{
name: "auth_store_v1",
- partialize: (state) => ({ isLoggedIn: state.isLoggedIn }),
+ partialize: (state) => ({ isLoggedIn: state.isLoggedIn, user: state.user }),
}
)
);
diff --git a/vite.config.js b/vite.config.js
index 23d7469..7694968 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -12,6 +12,11 @@ export default defineConfig({
// اجازه ارسال کوکیها
configure: (proxy) => {
proxy.on("proxyReq", (proxyReq, req) => {
+ console.log("🔵 Proxy Request:", {
+ method: req.method,
+ url: req.url,
+ headers: req.headers
+ });
const origin = req.headers.origin;
if (origin) {
proxyReq.setHeader("origin", origin);
@@ -20,14 +25,24 @@ export default defineConfig({
proxyReq.setHeader("Access-Control-Allow-Credentials", "true");
});
- proxy.on("proxyRes", (proxyRes) => {
+ proxy.on("proxyRes", (proxyRes, req) => {
+ console.log("🟢 Proxy Response:", {
+ statusCode: proxyRes.statusCode,
+ statusMessage: proxyRes.statusMessage,
+ headers: proxyRes.headers,
+ url: req.url
+ });
// اطمینان از دریافت کوکی از سرور
proxyRes.headers["Access-Control-Allow-Origin"] = "http://localhost:5173";
proxyRes.headers["Access-Control-Allow-Credentials"] = "true";
});
- proxy.on("error", (err) => {
- console.log("Proxy Error:", err);
+ proxy.on("error", (err, req) => {
+ console.error("🔴 Proxy Error:", {
+ error: err,
+ url: req?.url,
+ method: req?.method
+ });
});
},
},