first commit

This commit is contained in:
Your NamebaishaliHolocron
2026-06-15 12:57:03 +05:30
commit b9ac5ae0b2
398 changed files with 49583 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
"use server";
import { getValidAccessToken } from "ikoncomponents";
import { redirect } from "next/navigation";
export interface ExtraOptionsProps {
isAccessTokenRequird?: boolean;
revalidatePaths?: string[];
isReturnErrorResult?: boolean;
}
export const baseApiRequest = async (
url: string,
init: RequestInit,
extraOptions?: ExtraOptionsProps,
) => {
const headers = new Headers(init.headers);
if (!headers.get("Content-Type")) {
headers.set("Content-Type", "application/json");
}
if (extraOptions?.isAccessTokenRequird !== false) {
const accessToken = await getValidAccessToken(
process.env.NEXT_PUBLIC_IKON_API_URL || "",
);
if (!accessToken?.trim()) {
redirect("/login.html");
}
headers.set("Authorization", `Bearer ${accessToken.trim()}`);
}
const response = await fetch(url, {
...init,
headers,
});
const contentType = response.headers.get("Content-Type");
if (!response.ok) {
let body = "";
try {
body = contentType?.includes("application/json")
? await response.json()
: await response.text();
} catch {
/* ignore */
}
const message =
typeof body === "string"
? body
: (() => {
try {
return JSON.stringify(body);
} catch {
return String(body);
}
})();
console.error(
`API Request Failed - ${init.method} ${url}: HTTP ${response.status} ${message}`,
);
if (extraOptions?.isReturnErrorResult) {
return body;
}
throw new Error(`HTTP ${response.status}: ${message}`);
}
return contentType?.includes("application/json")
? await response.json()
: await response.text();
};

View File

@@ -0,0 +1,36 @@
import { baseApiRequest } from "../../apiRequests/baseApiRequest";
import { GradeResponse, GradeResponseDto } from "./type";
const PROJECT_MANAGEMENT_BASE_URL = `${process.env.NEXT_PUBLIC_PROJECT_MANAGEMENT_API_URL}/api/v1`;
export const getAllGrade = async (): Promise<GradeResponse> => {
return await baseApiRequest(
`${PROJECT_MANAGEMENT_BASE_URL}/grades`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
credentials: "include",
},
{
isAccessTokenRequird: true,
revalidatePaths: ["/grades"],
},
);
};
export const getGradeById = async (id: string): Promise<GradeResponseDto> => {
return await baseApiRequest(
`${PROJECT_MANAGEMENT_BASE_URL}/grades/${id}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
},
{
isAccessTokenRequird: true,
revalidatePaths: [`/grades/${id}`],
},
);
};

View File

@@ -0,0 +1,34 @@
export interface GradeResponseDto {
id: string;
accountId: string;
grade: string;
}
export interface SortInfo {
empty: boolean;
sorted: boolean;
unsorted: boolean;
}
export interface PageableInfo {
pageNumber: number;
pageSize: number;
sort: SortInfo;
offset: number;
paged: boolean;
unpaged: boolean;
}
export interface GradeResponse {
content: GradeResponseDto[];
pageable: PageableInfo;
last: boolean;
totalPages: number;
totalElements: number;
sort: SortInfo;
first: boolean;
number: number;
size: number;
numberOfElements: number;
empty: boolean;
}

View File

@@ -0,0 +1,36 @@
import { baseApiRequest } from "../../apiRequests/baseApiRequest";
import { RoleResponse, RoleResponseDto } from "./type";
const PROJECT_MANAGEMENT_BASE_URL = `${process.env.NEXT_PUBLIC_PROJECT_MANAGEMENT_API_URL}/api/v1`;
export const getAllRoles = async (): Promise<RoleResponse> => {
return await baseApiRequest(
`${PROJECT_MANAGEMENT_BASE_URL}/roles`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
credentials: "include",
},
{
isAccessTokenRequird: true,
revalidatePaths: ["/roles"],
},
);
};
export const getRoleById = async (id: string): Promise<RoleResponseDto> => {
return await baseApiRequest(
`${PROJECT_MANAGEMENT_BASE_URL}/roles/${id}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
},
{
isAccessTokenRequird: true,
revalidatePaths: [`/roles/${id}`],
},
);
};

View File

@@ -0,0 +1,34 @@
export interface RoleResponseDto {
id: string;
accountId: string;
role: string;
}
export interface SortInfo {
empty: boolean;
sorted: boolean;
unsorted: boolean;
}
export interface PageableInfo {
pageNumber: number;
pageSize: number;
sort: SortInfo;
offset: number;
paged: boolean;
unpaged: boolean;
}
export interface RoleResponse {
content: RoleResponseDto[];
pageable: PageableInfo;
last: boolean;
totalPages: number;
totalElements: number;
sort: SortInfo;
first: boolean;
number: number;
size: number;
numberOfElements: number;
empty: boolean;
}

View File

@@ -0,0 +1,43 @@
import { baseApiRequest } from "../apiRequests/baseApiRequest";
const PROJECT_API = `${process.env.NEXT_PUBLIC_PROJECT_MANAGEMENT_API_URL}/api/v1`;
// const USER_API =
// "https://ikoncloud-dev.keross.com/ikon-api/platform/user/current";
// READ
export const getGrades = async () => {
return baseApiRequest(
`${PROJECT_API}/grades`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true },
);
};
export const getRoles = async () => {
return baseApiRequest(
`${PROJECT_API}/roles`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true },
);
};
export const getEmployees = async () => {
return baseApiRequest(
`${PROJECT_API}/employees`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
credentials: "include",
},
{
isAccessTokenRequird: true,
revalidatePaths: ["/roles"],
},
);
};

View File

@@ -0,0 +1,109 @@
import { baseApiRequest } from "../apiRequests/baseApiRequest";
import { FxRateResponse } from "./type";
// interface FxRateApiResponse {
// id: string;
// year: string;
// currency: string;
// rate: string;
// }
// interface PaginatedResponse<T> {
// content: T[];
// }
const PROJECT_MANAGEMENT_BASE_URL = `${process.env.NEXT_PUBLIC_PROJECT_MANAGEMENT_API_URL}/api/v1`;
export const getAllFXRate = async (): Promise<FxRateResponse> => {
return await baseApiRequest(
`${PROJECT_MANAGEMENT_BASE_URL}/fx-rate`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
credentials: "include",
},
{
isAccessTokenRequird: true,
revalidatePaths: ["/fx-rate"],
},
);
};
export const getFxRateByYear = async (
year: string,
): Promise<FxRateResponse> => {
return await baseApiRequest(
`${PROJECT_MANAGEMENT_BASE_URL}/fx-rate/year=${year}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
},
{
isAccessTokenRequird: true,
revalidatePaths: [`/fx-rate/year=${year}`],
},
);
};
// export type FxRateMap = Map<string, Map<string, number>>;
// export async function createFxRateMap(): Promise<FxRateMap> {
// const fxRateMap: FxRateMap = new Map();
// try {
// console.log("Fetching FX Rates for map...");
// const response = await baseApiRequest(
// "${process.env.SALES_CRM_API_URL}/fxrate",
// {
// method: "GET",
// credentials: "include",
// },
// );
// const paginatedData = response as PaginatedResponse<FxRateApiResponse>;
// if (!paginatedData || !Array.isArray(paginatedData.content)) {
// console.error(
// "Invalid response structure received from FX Rate API:",
// paginatedData,
// );
// throw new Error("Failed to fetch FX Rates: Invalid data format.");
// }
// const rates: FxRateApiResponse[] = paginatedData.content;
// console.log(`Processing ${rates.length} FX rates...`);
// rates.forEach((rateData) => {
// const year = rateData.year;
// const currency = rateData.currency.toUpperCase();
// const rateValue = parseFloat(rateData.rate);
// if (!year || !currency || isNaN(rateValue)) {
// console.warn("Skipping invalid rate data:", rateData);
// return;
// }
// if (!fxRateMap.has(year)) {
// fxRateMap.set(year, new Map<string, number>());
// }
// const yearMap = fxRateMap.get(year)!;
// yearMap.set(currency, rateValue);
// });
// console.log("FX Rate Map created successfully:", fxRateMap);
// return fxRateMap;
// } catch (error) {
// console.error("Error creating FX Rate Map:", error);
// let errorMessage = "An unknown error occurred";
// if (error instanceof Error) {
// errorMessage = error.message;
// } else if (typeof error === "string") {
// errorMessage = error;
// }
// throw new Error(`Failed to create FX Rate Map: ${errorMessage}`);
// }
// }

View File

@@ -0,0 +1,65 @@
export interface FxRateDetail {
id: string;
currency: string;
fxRate: number;
activeStatus: boolean;
year: string;
}
export interface FxRateYearGroup {
[year: string]: {
[key: string]: FxRateDetail;
};
}
export interface FxContentItem {
fxRateDetails: FxRateYearGroup;
}
export interface SortInfo {
empty: boolean;
sorted: boolean;
unsorted: boolean;
}
export interface PageableInfo {
pageNumber: number;
pageSize: number;
sort: SortInfo;
offset: number;
paged: boolean;
unpaged: boolean;
}
export interface FxRateEntry {
id: string;
currency: string;
fxRate: number;
activeStatus: boolean;
year: string;
}
export interface FxRateDetails {
[key: string]: {
[key: string]: FxRateEntry;
};
}
export interface FxRateResponse {
content: FxContentItem[];
pageable: PageableInfo;
last: boolean;
totalPages: number;
totalElements: number;
sort: SortInfo;
first: boolean;
number: number;
size: number;
numberOfElements: number;
empty: boolean;
}
export interface CurrencyOption {
value: string;
label: string;
}

View File

@@ -0,0 +1,55 @@
import { IssueData } from "../../interface/issue";
import { RiskData } from "../../interface/risk";
import { baseApiRequest } from "../apiRequests/baseApiRequest";
const ISSUE_API = `${process.env.NEXT_PUBLIC_PROJECT_MANAGEMENT_API_URL}/api/v1/issues`;
// CREATE
export const createIssueApi = async (issue: IssueData) => {
return baseApiRequest(
ISSUE_API,
{
method: "POST",
body: JSON.stringify(issue),
},
{ isAccessTokenRequird: true },
);
};
export const updateIssueApi = async (issueIdentifier: string, updatedIssue: IssueData) => {
return baseApiRequest(
`${ISSUE_API}/${issueIdentifier}`,
{
method: "PUT",
body: JSON.stringify(updatedIssue),
},
{ isAccessTokenRequird: true },
);
};
// READ
export const issuesApi = async (projectIdentifier: string) => {
return baseApiRequest(
`${ISSUE_API}/project/${projectIdentifier}`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true }
);
};
export const getIssueByIdentifierApi = async (issueId: string) => {
return baseApiRequest(
`${ISSUE_API}/${issueId}`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true }
);
};

View File

@@ -0,0 +1,69 @@
import { MeetingData } from "@/app/main/planning/projects/[projectIdentifier]/component/momTab/components/meetingForm/types";
import { baseApiRequest } from "../apiRequests/baseApiRequest";
const MEETING_API = `${process.env.NEXT_PUBLIC_PROJECT_MANAGEMENT_API_URL}/api/v1/meetings`;
// CREATE
export const createMeetingApi = async (meeting: MeetingData) => {
return baseApiRequest(
MEETING_API,
{
method: "POST",
body: JSON.stringify(meeting),
},
{ isAccessTokenRequird: true },
);
};
// READ ALL BY PROJECT ID
export const meetingsApi = async (projectIdentifier: string) => {
return baseApiRequest(
`${MEETING_API}/project/${projectIdentifier}`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true },
);
};
// READ BY ID
export const getMeetingByIdApi = async (meetingId: string) => {
return baseApiRequest(
`${MEETING_API}/${meetingId}`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true },
);
};
// UPDATE
export const updateMeetingApi = async (meetingId: string, updatedMeeting: MeetingData) => {
return baseApiRequest(
`${MEETING_API}/${meetingId}`,
{
method: "PUT",
body: JSON.stringify(updatedMeeting),
},
{ isAccessTokenRequird: true },
);
};
// DELETE
export const deleteMeetingApi = async (meetingId: string) => {
return baseApiRequest(
`${MEETING_API}/${meetingId}`,
{
method: "DELETE",
},
{ isAccessTokenRequird: true },
);
};

View File

@@ -0,0 +1,26 @@
import { baseApiRequest } from "../apiRequests/baseApiRequest";
import { DashboardWidgetsResponseDto, StatusWiseProjectResponseData } from "./types";
const PROJECT_DASHBOARD_API = `${process.env.NEXT_PUBLIC_PROJECT_MANAGEMENT_API_URL}/api/v1/dashboard`;
export const getWidgetsData: () => Promise<DashboardWidgetsResponseDto> = async () => {
return baseApiRequest(
`${PROJECT_DASHBOARD_API}/widgets`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true },
);
};
export const getStatusWiseProjectCount: () => Promise<StatusWiseProjectResponseData> = async () => {
return baseApiRequest(
`${PROJECT_DASHBOARD_API}/status-wise-projects`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true },
);
};

View File

@@ -0,0 +1,10 @@
export interface StatusWiseProjectResponseData {
status: string;
projectCount: number;
}
export interface DashboardWidgetsResponseDto {
totalProjects: number;
totalIssues: number;
totalRisksCount: number;
}

View File

@@ -0,0 +1,48 @@
import { ProjectFormData } from "@/app/main/planning/projects/projectModal/zodProject";
import { Project } from "@/app/main/planning/projects/types/project";
import { ProductOfProject } from "../../interface/productOfProject";
import { baseApiRequest } from "../apiRequests/baseApiRequest";
const PROJECT_API = `${process.env.NEXT_PUBLIC_PROJECT_MANAGEMENT_API_URL}/api/v1/projects`;
const USER_API =
"https://ikoncloud-dev.keross.com/ikon-api/platform/user/current";
// READ
export const getProductsApi = async (projectId: string) => {
return baseApiRequest(
`${PROJECT_API}/${projectId}/products`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true },
);
};
// UPDATE
export const updateProductApi = async (product: ProductOfProject) => {
return baseApiRequest(
`${PROJECT_API}/${product.projectIdentifier}/products/${product.productIdentifier}`,
{
method: "PUT",
body: JSON.stringify(product),
},
{ isAccessTokenRequird: true },
);
};
// WORKFLOW TRANSITION
export const transitionProductStatusApi = async (
projectId: string,
productId: string,
targetStatus: string,
) => {
return baseApiRequest(
`${PROJECT_API}/${projectId}/products/${productId}/transition`,
{
method: "POST",
body: JSON.stringify({ targetStatus }),
},
{ isAccessTokenRequird: true, isReturnErrorResult: true },
);
};

View File

@@ -0,0 +1,112 @@
export interface ProductPSData {
productIdentifier: string; // UUID
dealName: string;
projectManager: string;
productStatus: string;
updatedOn: string;
updatedBy: string;
dealStatus: string;
dealIdentifier: string; // UUID
leadIdentifier: string; // UUID
quotation: Record<string, QuotationItemDto>;
productType: string;
productDescription: string;
scheduleData: ScheduleDataDto;
resourceDataWithAllocation: ResourceAllocationDto[];
discountPercent: number | null;
expenseDetails: Record<string, ExpenseDetailDto>;
}
// --------------------------------------------------
export interface QuotationItemDto {
id: string;
role: string;
totalFTE: number;
scr: number;
expenses: number;
otherCosts: number;
billingAmount: number;
}
// --------------------------------------------------
export interface ScheduleDataDto {
task: TaskDto[];
dependency: DependencyDto[];
group: Record<string, GroupItem>;
}
export interface GroupItem {
id?: string | number;
name?: string;
type?: string;
order?: number;
color?: string;
isActive?: boolean;
}
// --------------------------------------------------
export interface TaskDto {
id: number;
parentId: number;
taskName: string;
taskDuration: number;
taskPredecessor: string;
dependencyType: number;
taskColour: string;
delayDuration: number;
taskDescription: string;
taskStart: string;
taskEnd: string;
milestoneTask: boolean;
}
// --------------------------------------------------
export interface DependencyDto {
id: number;
predecessorId: number;
dependencyType: number;
}
// --------------------------------------------------
export interface ResourceAllocationDto {
id?: string;
allocation: Record<string, number>;
detailedAllocation: Record<string, DetailedAllocationValue>;
resourceType: string;
role: string;
gradeId: number;
employeeName: string;
taskName: string;
resourceId: string;
taskId: number;
}
export interface DetailedAllocationValue {
hours?: number;
date?: string;
cost?: number;
comments?: string;
approved?: boolean;
}
export interface ExpenseDetailDto {
expenseName: string;
location: string;
currency: string;
cost: number;
quantity: number;
totalCost: number;
remarks: string;
}

View File

@@ -0,0 +1,48 @@
import { UUID } from "crypto";
import { baseApiRequest } from "../apiRequests/baseApiRequest";
const PROJECT_API = `${process.env.NEXT_PUBLIC_PROJECT_MANAGEMENT_API_URL}/api/v1/projects`;
const USER_API =
"https://ikoncloud-dev.keross.com/ikon-api/platform/user/current";
// READ
export const getAllProductsByProject = async (projectIdentifier: UUID) => {
return baseApiRequest(
`${PROJECT_API}/${projectIdentifier}/products`,
{
method: "GET",
cache: "no-store", // optional but recommended for fresh data
},
{ isAccessTokenRequird: true },
);
};
export const getProductById = async (projectId: UUID, productIdentifier: UUID) => {
return baseApiRequest(
`${PROJECT_API}/${projectId}/products/${productIdentifier}`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true }
);
};
// UPDATE
export const updateProduct = async (projectId: UUID, productIdentifier: UUID, updatedProduct: any) => {
return baseApiRequest(
`${PROJECT_API}/${projectId}/products/${productIdentifier}`,
{
method: "PUT",
body: JSON.stringify(updatedProduct),
},
{ isAccessTokenRequird: true },
);
};

View File

@@ -0,0 +1,85 @@
import { UUID } from "crypto";
import { baseApiRequest } from "../apiRequests/baseApiRequest";
const PRODUCT_API = `${process.env.NEXT_PUBLIC_PROJECT_MANAGEMENT_API_URL}/api/v1/products`;
// Allocation payload shape
export interface ProductAllocation {
id?: string;
allocation: Record<string, number>;
detailedAllocation: Record<string, Record<string, any>>;
resourceType: string;
role: string;
gradeId: number;
employeeName: string;
taskName: string;
resourceId: string;
taskId: number;
}
// List allocations for a product
export const getResourceAllocations = async (productIdentifier: UUID) => {
return baseApiRequest(
`${PRODUCT_API}/${productIdentifier}/resource-allocations`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true }
);
};
// Get a single allocation
export const getAllocation = async (productIdentifier: UUID, allocationIdentifier: UUID) => {
return baseApiRequest(
`${PRODUCT_API}/${productIdentifier}/resource-allocations/${allocationIdentifier}`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true }
);
};
// Create allocation
export const createAllocation = async (productIdentifier: UUID, allocation: ProductAllocation) => {
return baseApiRequest(
`${PRODUCT_API}/${productIdentifier}/resource-allocations`,
{
method: "POST",
body: JSON.stringify(allocation),
},
{ isAccessTokenRequird: true }
);
};
// Update allocation
export const updateAllocation = async (
productIdentifier: UUID,
allocationIdentifier: UUID,
updatedAllocation: ProductAllocation
) => {
return baseApiRequest(
`${PRODUCT_API}/${productIdentifier}/resource-allocations/${allocationIdentifier}`,
{
method: "PUT",
body: JSON.stringify(updatedAllocation),
},
{ isAccessTokenRequird: true }
);
};
// Delete allocation
export const deleteAllocation = async (productIdentifier: UUID, allocationIdentifier: UUID) => {
return baseApiRequest(
`${PRODUCT_API}/${productIdentifier}/resource-allocations/${allocationIdentifier}`,
{
method: "DELETE",
},
{ isAccessTokenRequird: true }
);
};

View File

@@ -0,0 +1,111 @@
import { ProjectFormData } from "@/app/main/planning/projects/projectModal/zodProject";
import { baseApiRequest } from "../apiRequests/baseApiRequest";
import { Project } from "@/app/main/planning/projects/types/project";
const PROJECT_API = `${process.env.NEXT_PUBLIC_PROJECT_MANAGEMENT_API_URL}/api/v1/projects`;
const USER_API =
`${process.env.NEXT_PUBLIC_IKON_API_URL}/platform/user/current`;
// CREATE
export const createProjectApi = async (project: ProjectFormData) => {
return baseApiRequest(
PROJECT_API,
{
method: "POST",
body: JSON.stringify(project),
},
{ isAccessTokenRequird: true },
);
};
// READ
export const getProjectsApi = async () => {
return baseApiRequest(
PROJECT_API,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true },
);
};
export const getProjectByIdentifierApi = async (projectId: string) => {
return baseApiRequest(
`${PROJECT_API}/${projectId}`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true },
);
};
// UPDATE
export const updateProjectApi = async (project: Project) => {
return baseApiRequest(
`${PROJECT_API}/${project.projectIdentifier}`,
{
method: "PUT",
body: JSON.stringify(project),
},
{ isAccessTokenRequird: true },
);
};
export const getScheduleApi = async (projectId: string) => {
return baseApiRequest(
`${PROJECT_API}/${projectId}/schedules`,
{
method: "GET",
cache: "no-store"
},
{ isAccessTokenRequird: true },
);
};
export const getAllSchedulesApi = async () => {
return baseApiRequest(
`${PROJECT_API}/schedules`,
{
method: "GET",
cache: "no-store"
},
{ isAccessTokenRequird: true },
);
};
export const saveScheduleApi = async (
projectId: string,
scheduleData: { task: any[]; dependency: any[]; group?: Record<string, any> },
) => {
return baseApiRequest(
`${PROJECT_API}/${projectId}/schedules`,
{ method: "PUT", body: JSON.stringify(scheduleData) },
{ isAccessTokenRequird: true },
);
};
export const getUsersApi = async () => {
return baseApiRequest(
USER_API,
{
method: "GET",
cache: "no-store",
},
{
isAccessTokenRequird: true,
},
);
};
export const getAllActiveProjectsTimelineApi = async () => {
return baseApiRequest(
`${PROJECT_API}/active-projects-timeline`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true },
);
};

View File

@@ -0,0 +1,88 @@
import { baseApiRequest } from "../apiRequests/baseApiRequest";
import { ManagerMemberships, Role, RoleMembership } from "./projectManager";
const API_BASE_URL = "https://ikoncloud-dev.keross.com/ikon-api";
export const getAllRoles = async (): Promise<Role[]> => {
return await baseApiRequest(
`${API_BASE_URL}/role`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
credentials: "include",
},
{
isAccessTokenRequird: true,
}
);
};
export const getRoleMemberships = async (roleId: string): Promise<RoleMembership[]> => {
return await baseApiRequest(
`${API_BASE_URL}/role/${roleId}/membership`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
credentials: "include",
},
{
isAccessTokenRequird: true,
}
);
};
export const getRoleIdByName = (
roles: Role[],
roleName: string
): string | undefined => {
if (!roles || !Array.isArray(roles)) {
console.error("getRoleIdByName received invalid roles data:", roles);
return undefined;
}
const foundRole = roles.find((role) => role.roleName === roleName);
return foundRole?.roleId;
};
export const getManagerMemberships = async (): Promise<ManagerMemberships> => {
try {
const response = await getAllRoles();
const allRoles = Array.isArray(response) ? response : (response?.content || []);
const projectManagerId = getRoleIdByName(allRoles, "Project manager");
const accountManagerId = getRoleIdByName(allRoles, "Account Manager");
if (!projectManagerId) {
console.warn("Could not find roleId for 'Project Manager'");
}
if (!accountManagerId) {
console.warn("Could not find roleId for 'Account Manager'");
}
const pmPromise = projectManagerId
? getRoleMemberships(projectManagerId)
: Promise.resolve([]);
const amPromise = accountManagerId
? getRoleMemberships(accountManagerId)
: Promise.resolve([]);
const [projectManagerMembers, accountManagerMembers] = await Promise.all([
pmPromise,
amPromise,
]);
return {
projectManagerMembers,
accountManagerMembers,
};
} catch (error) {
console.error("Error in getManagerMemberships:", error);
return {
projectManagerMembers: [],
accountManagerMembers: [],
};
}
};

View File

@@ -0,0 +1,54 @@
export interface IkonGroup {
groupId: string;
groupName: string;
softwareId: string;
accountId: string | null;
groupType: "STATIC" | "DYNAMIC" | string;
active: boolean;
groupDescription: string;
}
export interface Role {
roleId: string;
roleName: string;
active: boolean;
softwareId: string;
roleDescription: string;
ikonGroups: IkonGroup[];
}
export interface RoleMembership {
roleMembershipId: string;
userId: string;
accountId: string;
roleId: string;
}
export interface ManagerMemberships {
projectManagerMembers: RoleMembership[];
accountManagerMembers: RoleMembership[];
}
export interface User {
userId: string;
userName: string;
userLogin: string;
userPhone: string | null;
userEmail: string;
userThumbnail: string | null;
userType: string;
active: boolean;
dateOfBirth: string | null;
userProfileImage: string | null;
userDescription: string | null;
userDesignation: string | null;
invitedUser: boolean;
userDeleted: boolean;
}
export interface UserInfo {
userName: string;
userLogin: string;
userEmail: string;
}

View File

@@ -0,0 +1,54 @@
import { RiskData } from "../../interface/risk";
import { baseApiRequest } from "../apiRequests/baseApiRequest";
const RISK_API = `${process.env.NEXT_PUBLIC_PROJECT_MANAGEMENT_API_URL}/api/v1/risks`;
// CREATE
export const createRiskApi = async (Risk: RiskData) => {
return baseApiRequest(
RISK_API,
{
method: "POST",
body: JSON.stringify(Risk),
},
{ isAccessTokenRequird: true },
);
};
export const updateRiskApi = async (riskIdentifier: string, updatedRisk: RiskData) => {
return baseApiRequest(
`${RISK_API}/${riskIdentifier}`,
{
method: "PUT",
body: JSON.stringify(updatedRisk),
},
{ isAccessTokenRequird: true },
);
};
// READ
export const risksApi = async () => {
return baseApiRequest(
RISK_API,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true },
);
};
export const getRiskByIdentifierApi = async (riskId: string) => {
return baseApiRequest(
`${RISK_API}/${riskId}`,
{
method: "GET",
cache: "no-store",
},
{ isAccessTokenRequird: true }
);
};

View File

@@ -0,0 +1,65 @@
import { baseApiRequest } from "../apiRequests/baseApiRequest";
import { Role, User, RoleMembership } from "./type";
const API_BASE_URL = process.env.NEXT_PUBLIC_PROJECT_MANAGEMENT_API_URL;
const IKON_API_URL = process.env.NEXT_PUBLIC_IKON_API_URL;
export const getAllRoles = async (): Promise<Role[]> => {
return await baseApiRequest(
`${API_BASE_URL}/role`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
credentials: "include",
},
{
isAccessTokenRequird: true,
revalidatePaths: ["/roles"],
}
);
};
export const getAllUsers = async (): Promise<User[]> => {
return await baseApiRequest(
`${IKON_API_URL}/platform/user/current`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
credentials: "include",
},
{
isAccessTokenRequird: true,
revalidatePaths: ["/platform/user/current"],
}
);
};
export const getUserById = async (userId: string): Promise<User | null> => {
return await baseApiRequest(
`${IKON_API_URL}/platform/user/${userId}`, // Assuming this endpoint pattern
{
method: "GET",
headers: { "Content-Type": "application/json" },
credentials: "include",
},
{
isAccessTokenRequird: true,
revalidatePaths: [`/platform/user/${userId}`],
}
);
};
export const getRoleMemberships = async (roleId: string): Promise<RoleMembership[]> => {
return await baseApiRequest(
`${API_BASE_URL}/role/${roleId}/membership`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
credentials: "include",
},
{
isAccessTokenRequird: true,
}
);
};

View File

@@ -0,0 +1,145 @@
import { getAllRoles, getAllUsers, getRoleMemberships, getUserById } from ".";
import { ManagerMemberships, Role, UserInfo } from "./type"; // Import User
export const getRoleIdByName = (
roles: Role[],
roleName: string
): string | undefined => {
if (!roles || !Array.isArray(roles)) {
console.error("getRoleIdByName received invalid roles data:", roles);
return undefined;
}
const foundRole = roles.find((role) => role.roleName === roleName);
return foundRole?.roleId;
};
export const findUsersInRole = async (roleName: string) => {
try {
console.log("Step 1: Fetching all roles...");
const allRoles = await getAllRoles();
console.log(`Step 2: Finding ID for role "${roleName}"...`);
const roleId = getRoleIdByName(allRoles, roleName);
if (!roleId) {
console.error(`Error: Role "${roleName}" not found.`);
return;
}
console.log(`Found roleId: ${roleId}`);
console.log("Step 3: Fetching memberships for this role...");
const memberships = await getRoleMemberships(roleId);
if (memberships.length === 0) {
console.log(`No users found for role "${roleName}".`);
return;
}
console.log(
`Success! Found ${memberships.length} user(s) in "${roleName}":`
);
const userIds = memberships.map((m: { userId: string }) => m.userId);
console.log(userIds);
return userIds;
} catch (error) {
console.error("An error occurred during the process:", error);
}
};
export const getManagerMemberships = async (): Promise<ManagerMemberships> => {
try {
const response = await getAllRoles();
const allRoles = Array.isArray(response) ? response : (response?.content || []);
console.log("rolesss", allRoles);
const projectManagerId = getRoleIdByName(allRoles, "Project manager");
const accountManagerId = getRoleIdByName(allRoles, "Account Manager");
if (!projectManagerId) {
console.warn("Could not find roleId for 'Project Manager'");
}
if (!accountManagerId) {
console.warn("Could not find roleId for 'Account Manager'");
}
const pmPromise = projectManagerId
? getRoleMemberships(projectManagerId)
: Promise.resolve([]);
const amPromise = accountManagerId
? getRoleMemberships(accountManagerId)
: Promise.resolve([]);
const [projectManagerMembers, accountManagerMembers] = await Promise.all([
pmPromise,
amPromise,
]);
return {
projectManagerMembers,
accountManagerMembers,
};
} catch (error) {
console.error("Error in getManagerMemberships:", error);
return {
projectManagerMembers: [],
accountManagerMembers: [],
};
}
};
export const createUserMaps = async (): Promise<{
nameMap: Map<string, string>;
designationMap: Map<string, string | null>;
}> => {
const nameMap = new Map<string, string>();
const designationMap = new Map<string, string | null>();
try {
const response = await getAllUsers();
const userArray = response ?? response;
if (!userArray || !Array.isArray(userArray) || userArray.length === 0) {
return { nameMap, designationMap };
} else {
for (const user of userArray) {
if (user?.userId) {
nameMap.set(user.userId, user.userName);
designationMap.set(user.userId, user.userDesignation);
}
}
}
return { nameMap, designationMap };
} catch (error) {
console.error("Failed to fetch users and create maps:", error);
return { nameMap, designationMap };
}
};
export const getUserNameById = async (
userId: string
): Promise<string | undefined> => {
try {
const user = await getUserById(userId);
return user?.userName;
} catch (error) {
console.error("Failed to get user name by ID:", error);
}
};
export const getUserInfo = async (userId: string): Promise<UserInfo | null> => {
const user = await getUserById(userId);
if (!user) {
return null;
}
return {
userName: user.userName,
userLogin: user.userLogin,
userEmail: user.userEmail,
};
};

View File

@@ -0,0 +1,54 @@
export interface IkonGroup {
groupId: string;
groupName: string;
softwareId: string;
accountId: string | null;
groupType: "STATIC" | "DYNAMIC" | string;
active: boolean;
groupDescription: string;
}
export interface Role {
roleId: string;
roleName: string;
active: boolean;
softwareId: string;
roleDescription: string;
ikonGroups: IkonGroup[];
}
export interface RoleMembership {
roleMembershipId: string;
userId: string;
accountId: string;
roleId: string;
}
export interface ManagerMemberships {
projectManagerMembers: RoleMembership[];
accountManagerMembers: RoleMembership[];
}
export interface User {
userId: string;
userName: string;
userLogin: string;
userPhone: string | null;
userEmail: string;
userThumbnail: string | null;
userType: string;
active: boolean;
dateOfBirth: string | null;
userProfileImage: string | null;
userDescription: string | null;
userDesignation: string | null;
invitedUser: boolean;
userDeleted: boolean;
}
export interface UserInfo {
userName: string;
userLogin: string;
userEmail: string;
}

View File

@@ -0,0 +1,38 @@
import { baseApiRequest } from "../apiRequests/baseApiRequest";
import { WorkingDaysResponse } from "./type";
const PROJECT_MANAGEMENT_BASE_URL = `${process.env.NEXT_PUBLIC_PROJECT_MANAGEMENT_API_URL}/api/v1`;
export const getAllWorkingDays = async (): Promise<WorkingDaysResponse> => {
return await baseApiRequest(
`${PROJECT_MANAGEMENT_BASE_URL}/working-days`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
credentials: "include",
},
{
isAccessTokenRequird: true,
revalidatePaths: ["/working-days"],
},
);
};
export const getWorkingDaysByYear = async (
year: string,
): Promise<WorkingDaysResponse> => {
return await baseApiRequest(
`${PROJECT_MANAGEMENT_BASE_URL}/working-days/year/${year}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
},
{
isAccessTokenRequird: true,
revalidatePaths: [`/working-days/year/${year}`],
},
);
};

View File

@@ -0,0 +1,40 @@
export interface WorkingDaysMonthDto {
year: string;
month: string;
workingDays: number;
}
export interface WorkingDaysResponseDto {
workingId: string;
accountIdentifier: string;
workingDaysDetails: Record<string, Record<string, WorkingDaysMonthDto>>;
}
export interface WorkingDaysPageableInfo {
offset: number;
sort: SortInfo;
pageSize: number;
paged: boolean;
unpaged: boolean;
pageNumber: number;
}
export interface SortInfo {
empty: boolean;
sorted: boolean;
unsorted: boolean;
}
export interface WorkingDaysResponse {
content: WorkingDaysResponseDto[];
pageable: WorkingDaysPageableInfo;
last: boolean;
totalPages: number;
totalElements: number;
sort: SortInfo;
first: boolean;
number: number;
size: number;
numberOfElements: number;
empty: boolean;
}

View File

@@ -0,0 +1,186 @@
"use client";
import React, {
createContext,
useContext,
useEffect,
useState,
ReactNode,
} from "react";
import { getManagerMemberships } from "@/app/utils/api/user-dashboard-platform/mappingFunction";
import { getAllUsers } from "@/app/utils/api/user-dashboard-platform";
import type {
RoleMembership,
User,
} from "@/app/utils/api/user-dashboard-platform/type";
import { getAllFXRate } from "@/app/utils/api/fxRate";
import type { FxRateResponse } from "@/app/utils/api/fxRate/type";
/** year -> currency (upper-cased) -> rate */
export type FxRateMap = Map<string, Map<string, number>>;
type SelectOption = { value: string; label: string };
interface AppCacheState {
/** userId -> userName */
userNameMap: Map<string, string>;
/** userId -> designation */
userDesignationMap: Map<string, string | null>;
/** all active (non-deleted) platform users */
activeUsers: User[];
/** year -> currency -> rate, derived from the raw FX response */
fxRateMap: FxRateMap;
/** the raw FX rate response (paginated) */
fxRateResponse: FxRateResponse | null;
/** userId/userName options for users holding the Project Manager role */
projectManagerOptions: SelectOption[];
/** userId/userName options for users holding the Account Manager role */
accountManagerOptions: SelectOption[];
isLoading: boolean;
refresh: () => void;
}
const AppCacheContext = createContext<AppCacheState>({
userNameMap: new Map(),
userDesignationMap: new Map(),
activeUsers: [],
fxRateMap: new Map(),
fxRateResponse: null,
projectManagerOptions: [],
accountManagerOptions: [],
isLoading: true,
refresh: () => {},
});
export function AppCacheProvider({ children }: { children: ReactNode }) {
const [userNameMap, setUserNameMap] = useState<Map<string, string>>(new Map());
const [userDesignationMap, setUserDesignationMap] = useState<
Map<string, string | null>
>(new Map());
const [activeUsers, setActiveUsers] = useState<User[]>([]);
const [fxRateMap, setFxRateMap] = useState<FxRateMap>(new Map());
const [fxRateResponse, setFxRateResponse] = useState<FxRateResponse | null>(
null,
);
const [projectManagerOptions, setProjectManagerOptions] = useState<
SelectOption[]
>([]);
const [accountManagerOptions, setAccountManagerOptions] = useState<
SelectOption[]
>([]);
const [isLoading, setIsLoading] = useState(true);
const [refreshKey, setRefreshKey] = useState(0);
useEffect(() => {
let cancelled = false;
const load = async () => {
setIsLoading(true);
try {
const allUsers = await getAllUsers();
const usersList: User[] = Array.isArray(allUsers) ? allUsers : [];
// Build name + designation maps and the active-user list from one fetch
const nameMap = new Map<string, string>();
const designationMap = new Map<string, string | null>();
usersList.forEach((u) => {
if (u?.userId) {
nameMap.set(u.userId, u.userName);
designationMap.set(u.userId, u.userDesignation);
}
});
const activeUserList = usersList.filter(
(u) => u.active && !u.userDeleted,
);
const [memberships, rawFxResponse] = await Promise.all([
getManagerMemberships(),
getAllFXRate(),
]);
// Build FxRateMap (year -> currency -> rate) from the raw response
const builtFxRateMap: FxRateMap = new Map();
rawFxResponse.content?.forEach((item) => {
Object.entries(item.fxRateDetails || {}).forEach(
([year, currencyData]) => {
if (!currencyData) return;
Object.entries(currencyData).forEach(([, rateData]) => {
const currency = rateData?.currency?.toUpperCase();
const rateValue = rateData?.fxRate;
if (
!year ||
!currency ||
rateValue === null ||
rateValue === undefined ||
isNaN(rateValue)
) {
return;
}
if (!builtFxRateMap.has(year)) {
builtFxRateMap.set(year, new Map());
}
builtFxRateMap.get(year)!.set(currency, rateValue);
});
},
);
});
// Build manager option lists, deduped by userId
const toOptions = (members: RoleMembership[]): SelectOption[] => {
const options: SelectOption[] = [];
members.forEach((member) => {
const name = nameMap.get(member.userId);
if (name) options.push({ value: member.userId, label: name });
});
return Array.from(
new Map(options.map((opt) => [opt.value, opt])).values(),
);
};
if (!cancelled) {
setUserNameMap(nameMap);
setUserDesignationMap(designationMap);
setActiveUsers(activeUserList);
setFxRateMap(builtFxRateMap);
setFxRateResponse(rawFxResponse);
setProjectManagerOptions(toOptions(memberships.projectManagerMembers));
setAccountManagerOptions(toOptions(memberships.accountManagerMembers));
}
} catch (err) {
console.error("AppCacheProvider: Failed to load maps", err);
} finally {
if (!cancelled) setIsLoading(false);
}
};
load();
return () => {
cancelled = true;
};
}, [refreshKey]);
const refresh = () => setRefreshKey((k) => k + 1);
return (
<AppCacheContext.Provider
value={{
userNameMap,
userDesignationMap,
activeUsers,
fxRateMap,
fxRateResponse,
projectManagerOptions,
accountManagerOptions,
isLoading,
refresh,
}}
>
{children}
</AppCacheContext.Provider>
);
}
export function useAppCache(): AppCacheState {
return useContext(AppCacheContext);
}

View File

@@ -0,0 +1,34 @@
import { getUsersApi } from "../../api/projectApi";
import { User } from "../../api/projectManager/projectManager";
let userCache: Record<string, string> | null = null;
export async function buildUserMap(): Promise<Record<string, string>> {
if (userCache) return userCache;
const users = await getUsersApi();
const map: Record<string, string> = {};
users.forEach((user: User) => {
map[user.userId] = user.userName;
});
userCache = map;
return map;
}
export async function getUserNameById(userId: string): Promise<string> {
if (!userId) return "—";
const map = await buildUserMap();
return map[userId] || "Unknown User";
}
export async function getUserNamesByIds(ids: string[]): Promise<string[]> {
const map = await buildUserMap();
return ids.map(id => map[id] || "Unknown User");
}

View File

@@ -0,0 +1,16 @@
export function calculateDurationInDays(
startDate?: string | Date,
endDate?: string | Date
): number | null {
if (!startDate || !endDate) return null;
const start = new Date(startDate);
const end = new Date(endDate);
if (isNaN(start.getTime()) || isNaN(end.getTime())) return null;
const diffTime = end.getTime() - start.getTime();
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return diffDays >= 0 ? diffDays : null;
}

View File

@@ -0,0 +1,10 @@
export interface Expense {
expenseIdentifier?: string;
expenseName: string;
location: string;
currency: string;
cost: number;
quantity: number;
totalCost: number;
remarks: string;
}

View File

@@ -0,0 +1,35 @@
export interface IssueData {
issueId?: string;
issueTitle: string;
issueProbability: number;
grossIssueValue: number;
probableIssueValue: number;
probableIssueValueInUSD: number;
issueImpact: string;
issueOwner: string;
issueDescription: string;
mitigationAction?: string;
projectIdentifier: string;
financialIssue: boolean;
issueCreatedDate?: string;
issueOptionsSelectId?: string;
issueStatus?: string;
issueAge?: number;
effectedSprintId?: string;
}

View File

@@ -0,0 +1,48 @@
import { Expense } from "./expense";
export interface ProductOfProject {
productIdentifier: string;
projectIdentifier: string;
projectName: string;
projectManager: string;
accountId: string;
leadIdentifier: string;
productStatus: string;
projectStatus: string;
productType: string;
productDescription: string;
discountPercent: number;
resourceDataWithAllocation: ResourceAllocationDto[];
scheduleData?: { projectIdentifier?: string; productIdentifier?: string; task: any[]; group?: Record<string, any>; dependency?: any[] } | null;
createdOn: string;
createdBy: string;
updatedBy: string;
updatedOn: string;
expenseDetails: Record<string, Expense>;
}
export interface ResourceAllocationDto {
id?: string;
allocation: Record<string, number>;
detailedAllocation: Record<string, DetailedAllocationValue>;
resourceType: string;
role: string;
gradeId: number;
employeeName: string;
taskName: string;
resourceId: string;
taskId: number;
}
export interface DetailedAllocationValue {
hours?: number;
date?: string;
cost?: number;
comments?: string;
approved?: boolean;
}

View File

@@ -0,0 +1,46 @@
export interface Project {
projectIdentifier: string;
projectManager: string;
projectName: string;
projectStatus: string;
projectNumber: string;
parentProjectNo: string;
contractNumber: string;
projectClient: string;
projectCity: string;
projectCountry: string;
currency: string;
projectImage: string;
contractUpload: string;
source: string;
productType: string;
expenses: string;
formattedActualRevenueIncludingVAT_deal: string;
isCompleted: boolean;
groupNotExist: boolean;
isDebtRevenue_deal: boolean;
projectDescription: string;
createdById: string;
updatedBy: string;
projectManagerDelegates: string;
projectStartDate: string;
contractedStartDate: string;
contractedEndDate: string;
createdOn: string;
updatedOn: string;
projectTeam: string[];
projectTeamUnderProjectManager: string[];
projectTeamUnderProjectManagerDelegates: string[];
groupAssigneesEditStr: string;
groupAssigneesViewStr: string;
participants: Record<string, any>;
contractedProductIdentifierWiseDataObj: Record<string, any>;
productIdentifier: string;
}

View File

@@ -0,0 +1,18 @@
export interface RiskData {
riskIdentifier?: string;
riskTitle?: string;
riskProbability: number;
grossRiskValue?: number;
probableRiskValue?: number;
probableRiskValueInUSD?: number;
riskImpact?: string;
riskOwner?: string;
riskDescription?: string;
projectIdentifier?: string;
financialRisk?: boolean;
riskCreatedDate?: string;
riskOptionsSelectId?: string;
riskStatus?: string;
riskAge?: number;
effectedSprintId?: string
}

View File

@@ -0,0 +1,14 @@
export function formatDate(inputDate: string | null | undefined): string {
if (!inputDate) return "";
const date = new Date(inputDate);
const day = String(date.getDate()).padStart(2, "0");
const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const month = monthNames[date.getMonth()];
const year = date.getFullYear();
return `${day}-${month}-${year}`;
}

View File

@@ -0,0 +1,197 @@
export const CountryDetails = [
{ code: "AF", name: "Afghanistan" },
{ code: "AL", name: "Albania" },
{ code: "DZ", name: "Algeria" },
{ code: "AD", name: "Andorra" },
{ code: "AO", name: "Angola" },
{ code: "AG", name: "Antigua and Barbuda" },
{ code: "AR", name: "Argentina" },
{ code: "AM", name: "Armenia" },
{ code: "AU", name: "Australia" },
{ code: "AT", name: "Austria" },
{ code: "AZ", name: "Azerbaijan" },
{ code: "BS", name: "Bahamas" },
{ code: "BH", name: "Bahrain" },
{ code: "BD", name: "Bangladesh" },
{ code: "BB", name: "Barbados" },
{ code: "BY", name: "Belarus" },
{ code: "BE", name: "Belgium" },
{ code: "BZ", name: "Belize" },
{ code: "BJ", name: "Benin" },
{ code: "BT", name: "Bhutan" },
{ code: "BO", name: "Bolivia" },
{ code: "BA", name: "Bosnia and Herzegovina" },
{ code: "BW", name: "Botswana" },
{ code: "BR", name: "Brazil" },
{ code: "BN", name: "Brunei" },
{ code: "BG", name: "Bulgaria" },
{ code: "BF", name: "Burkina Faso" },
{ code: "BI", name: "Burundi" },
{ code: "CV", name: "Cabo Verde" },
{ code: "KH", name: "Cambodia" },
{ code: "CM", name: "Cameroon" },
{ code: "CA", name: "Canada" },
{ code: "CF", name: "Central African Republic" },
{ code: "TD", name: "Chad" },
{ code: "CL", name: "Chile" },
{ code: "CN", name: "China" },
{ code: "CO", name: "Colombia" },
{ code: "KM", name: "Comoros" },
{ code: "CG", name: "Congo" },
{ code: "CD", name: "Congo (Democratic Republic)" },
{ code: "CR", name: "Costa Rica" },
{ code: "HR", name: "Croatia" },
{ code: "CU", name: "Cuba" },
{ code: "CY", name: "Cyprus" },
{ code: "CZ", name: "Czechia" },
{ code: "DK", name: "Denmark" },
{ code: "DJ", name: "Djibouti" },
{ code: "DM", name: "Dominica" },
{ code: "DO", name: "Dominican Republic" },
{ code: "EC", name: "Ecuador" },
{ code: "EG", name: "Egypt" },
{ code: "SV", name: "El Salvador" },
{ code: "GQ", name: "Equatorial Guinea" },
{ code: "ER", name: "Eritrea" },
{ code: "EE", name: "Estonia" },
{ code: "SZ", name: "Eswatini" },
{ code: "ET", name: "Ethiopia" },
{ code: "FJ", name: "Fiji" },
{ code: "FI", name: "Finland" },
{ code: "FR", name: "France" },
{ code: "GA", name: "Gabon" },
{ code: "GM", name: "Gambia" },
{ code: "GE", name: "Georgia" },
{ code: "DE", name: "Germany" },
{ code: "GH", name: "Ghana" },
{ code: "GR", name: "Greece" },
{ code: "GD", name: "Grenada" },
{ code: "GT", name: "Guatemala" },
{ code: "GN", name: "Guinea" },
{ code: "GW", name: "Guinea-Bissau" },
{ code: "GY", name: "Guyana" },
{ code: "HT", name: "Haiti" },
{ code: "HN", name: "Honduras" },
{ code: "HU", name: "Hungary" },
{ code: "IS", name: "Iceland" },
{ code: "IN", name: "India" },
{ code: "ID", name: "Indonesia" },
{ code: "IR", name: "Iran" },
{ code: "IQ", name: "Iraq" },
{ code: "IE", name: "Ireland" },
{ code: "IL", name: "Israel" },
{ code: "IT", name: "Italy" },
{ code: "JM", name: "Jamaica" },
{ code: "JP", name: "Japan" },
{ code: "JO", name: "Jordan" },
{ code: "KZ", name: "Kazakhstan" },
{ code: "KE", name: "Kenya" },
{ code: "KI", name: "Kiribati" },
{ code: "KW", name: "Kuwait" },
{ code: "KG", name: "Kyrgyzstan" },
{ code: "LA", name: "Laos" },
{ code: "LV", name: "Latvia" },
{ code: "LB", name: "Lebanon" },
{ code: "LS", name: "Lesotho" },
{ code: "LR", name: "Liberia" },
{ code: "LY", name: "Libya" },
{ code: "LI", name: "Liechtenstein" },
{ code: "LT", name: "Lithuania" },
{ code: "LU", name: "Luxembourg" },
{ code: "MG", name: "Madagascar" },
{ code: "MW", name: "Malawi" },
{ code: "MY", name: "Malaysia" },
{ code: "MV", name: "Maldives" },
{ code: "ML", name: "Mali" },
{ code: "MT", name: "Malta" },
{ code: "MH", name: "Marshall Islands" },
{ code: "MR", name: "Mauritania" },
{ code: "MU", name: "Mauritius" },
{ code: "MX", name: "Mexico" },
{ code: "FM", name: "Micronesia" },
{ code: "MD", name: "Moldova" },
{ code: "MC", name: "Monaco" },
{ code: "MN", name: "Mongolia" },
{ code: "ME", name: "Montenegro" },
{ code: "MA", name: "Morocco" },
{ code: "MZ", name: "Mozambique" },
{ code: "MM", name: "Myanmar" },
{ code: "NA", name: "Namibia" },
{ code: "NR", name: "Nauru" },
{ code: "NP", name: "Nepal" },
{ code: "NL", name: "Netherlands" },
{ code: "NZ", name: "New Zealand" },
{ code: "NI", name: "Nicaragua" },
{ code: "NE", name: "Niger" },
{ code: "NG", name: "Nigeria" },
{ code: "KP", name: "North Korea" },
{ code: "MK", name: "North Macedonia" },
{ code: "NO", name: "Norway" },
{ code: "OM", name: "Oman" },
{ code: "PK", name: "Pakistan" },
{ code: "PW", name: "Palau" },
{ code: "PS", name: "Palestine" },
{ code: "PA", name: "Panama" },
{ code: "PG", name: "Papua New Guinea" },
{ code: "PY", name: "Paraguay" },
{ code: "PE", name: "Peru" },
{ code: "PH", name: "Philippines" },
{ code: "PL", name: "Poland" },
{ code: "PT", name: "Portugal" },
{ code: "QA", name: "Qatar" },
{ code: "RO", name: "Romania" },
{ code: "RU", name: "Russia" },
{ code: "RW", name: "Rwanda" },
{ code: "KN", name: "Saint Kitts and Nevis" },
{ code: "LC", name: "Saint Lucia" },
{ code: "VC", name: "Saint Vincent and the Grenadines" },
{ code: "WS", name: "Samoa" },
{ code: "SM", name: "San Marino" },
{ code: "ST", name: "Sao Tome and Principe" },
{ code: "SA", name: "Saudi Arabia" },
{ code: "SN", name: "Senegal" },
{ code: "RS", name: "Serbia" },
{ code: "SC", name: "Seychelles" },
{ code: "SL", name: "Sierra Leone" },
{ code: "SG", name: "Singapore" },
{ code: "SK", name: "Slovakia" },
{ code: "SI", name: "Slovenia" },
{ code: "SB", name: "Solomon Islands" },
{ code: "SO", name: "Somalia" },
{ code: "ZA", name: "South Africa" },
{ code: "KR", name: "South Korea" },
{ code: "SS", name: "South Sudan" },
{ code: "ES", name: "Spain" },
{ code: "LK", name: "Sri Lanka" },
{ code: "SD", name: "Sudan" },
{ code: "SR", name: "Suriname" },
{ code: "SE", name: "Sweden" },
{ code: "CH", name: "Switzerland" },
{ code: "SY", name: "Syria" },
{ code: "TW", name: "Taiwan" },
{ code: "TJ", name: "Tajikistan" },
{ code: "TZ", name: "Tanzania" },
{ code: "TH", name: "Thailand" },
{ code: "TL", name: "Timor-Leste" },
{ code: "TG", name: "Togo" },
{ code: "TO", name: "Tonga" },
{ code: "TT", name: "Trinidad and Tobago" },
{ code: "TN", name: "Tunisia" },
{ code: "TR", name: "Turkey" },
{ code: "TM", name: "Turkmenistan" },
{ code: "TV", name: "Tuvalu" },
{ code: "UG", name: "Uganda" },
{ code: "UA", name: "Ukraine" },
{ code: "AE", name: "United Arab Emirates" },
{ code: "GB", name: "United Kingdom" },
{ code: "US", name: "United States" },
{ code: "UY", name: "Uruguay" },
{ code: "UZ", name: "Uzbekistan" },
{ code: "VU", name: "Vanuatu" },
{ code: "VA", name: "Vatican City" },
{ code: "VE", name: "Venezuela" },
{ code: "VN", name: "Vietnam" },
{ code: "YE", name: "Yemen" },
{ code: "ZM", name: "Zambia" },
{ code: "ZW", name: "Zimbabwe" },
];