Add Oudelaa dashboard API integration
فشلت بعض الفحوصات
Deploy To Ghaymah / deploy (push) Has been cancelled

هذا الالتزام موجود في:
boutmoun123
2026-05-25 20:36:52 +03:00
الأصل 367fce6557
التزام 8863f61d00
90 ملفات معدلة مع 16694 إضافات و1 حذوفات

عرض الملف

@@ -0,0 +1,97 @@
import { apiEndpoints } from "@/lib/api/endpoints";
import { fetchWithAuth } from "@/lib/auth/client";
import type {
AdminCreatePayload,
AdminUpdatePayload,
ApiRole,
ApiUser,
PaginatedResponse,
ProfileOverviewResponse,
SuccessMessage,
} from "@/types/api";
function normalizeUser(user: Partial<ApiUser> & { id?: string }) {
return {
...user,
_id: user._id ?? user.id ?? "",
} as ApiUser;
}
function normalizeUsersResponse(response: PaginatedResponse<ApiUser>) {
return {
...response,
items: Array.isArray(response.items) ? response.items.map((item) => normalizeUser(item)) : response.items,
data: Array.isArray(response.data) ? response.data.map((item) => normalizeUser(item)) : response.data,
};
}
export async function listPlatformAdmins(page = 1, limit = 20) {
const response = await fetchWithAuth<PaginatedResponse<ApiUser>>(
apiEndpoints.users.admins({ page, limit }),
);
return normalizeUsersResponse(response);
}
export async function getAdminUserById(userId: string) {
const response = await fetchWithAuth<ApiUser>(apiEndpoints.users.byId(userId));
return normalizeUser(response);
}
export async function updateAdminUser(userId: string, payload: AdminUpdatePayload) {
const response = await fetchWithAuth<ApiUser>(apiEndpoints.users.update(userId), {
method: "PATCH",
body: JSON.stringify(payload),
});
return normalizeUser(response);
}
export async function updatePlatformAdmin(userId: string, payload: AdminUpdatePayload) {
const response = await fetchWithAuth<ApiUser>(apiEndpoints.users.updateAdmin(userId), {
method: "PATCH",
body: JSON.stringify(payload),
});
return normalizeUser(response);
}
export async function setAdminUserRole(userId: string, role: ApiRole) {
const response = await fetchWithAuth<ApiUser>(apiEndpoints.users.setRole(userId), {
method: "PATCH",
body: JSON.stringify({ role }),
});
return normalizeUser(response);
}
export async function deleteAdminUser(userId: string) {
return fetchWithAuth<SuccessMessage>(apiEndpoints.users.remove(userId), { method: "DELETE" });
}
export async function deletePlatformAdmin(userId: string) {
return fetchWithAuth<SuccessMessage>(apiEndpoints.users.removeAdmin(userId), { method: "DELETE" });
}
export async function createAdminUser(payload: AdminCreatePayload) {
const response = await fetchWithAuth<ApiUser>(apiEndpoints.users.createAdmin, {
method: "POST",
body: JSON.stringify(payload),
});
return normalizeUser(response);
}
export async function searchAdminUsers(params: Record<string, string | number | boolean | null | undefined>) {
const response = await fetchWithAuth<PaginatedResponse<ApiUser>>(apiEndpoints.users.all(params));
return normalizeUsersResponse(response);
}
export async function discoverAdminUsers(params: Record<string, string | number | boolean | null | undefined>) {
const response = await fetchWithAuth<PaginatedResponse<ApiUser>>(apiEndpoints.users.discover(params));
return normalizeUsersResponse(response);
}
export async function searchPlatformAdmins(params: Record<string, string | number | boolean | null | undefined>) {
const response = await fetchWithAuth<PaginatedResponse<ApiUser>>(apiEndpoints.users.admins(params));
return normalizeUsersResponse(response);
}
export async function getProfileOverviewForSuperAdmin(userId: string) {
return fetchWithAuth<ProfileOverviewResponse>(apiEndpoints.users.profileOverview(userId));
}

عرض الملف

@@ -0,0 +1,7 @@
import { apiEndpoints } from "@/lib/api/endpoints";
import { fetchWithAuth } from "@/lib/auth/client";
import type { AuditLogsResponse } from "@/types/api";
export async function listAuditLogs(params: Record<string, string | number | boolean | null | undefined> = {}) {
return fetchWithAuth<AuditLogsResponse>(apiEndpoints.audit.logs(params));
}

عرض الملف

@@ -0,0 +1,34 @@
import { apiEndpoints } from "@/lib/api/endpoints";
import { fetchWithAuth } from "@/lib/auth/client";
import type { LoginResponse, SessionsResponse, SuccessMessage } from "@/types/api";
export async function loginDashboardUser(email: string, password: string) {
return fetchWithAuth<LoginResponse>(apiEndpoints.auth.login, {
method: "POST",
body: JSON.stringify({ email, password }),
});
}
export async function refreshDashboardUser(refreshToken: string) {
return fetchWithAuth<LoginResponse>(apiEndpoints.auth.refresh, {
method: "POST",
body: JSON.stringify({ refreshToken }),
});
}
export async function logoutDashboardUser(refreshToken: string) {
return fetchWithAuth<SuccessMessage>(apiEndpoints.auth.logout, {
method: "POST",
body: JSON.stringify({ refreshToken }),
});
}
export async function listSuperAdminSessions() {
return fetchWithAuth<SessionsResponse>(apiEndpoints.auth.superAdminSessions);
}
export async function revokeSuperAdminSession(sessionId: string) {
return fetchWithAuth<SuccessMessage>(apiEndpoints.auth.revokeSuperAdminSession(sessionId), {
method: "POST",
});
}

عرض الملف

@@ -0,0 +1,15 @@
import { apiEndpoints } from "@/lib/api/endpoints";
import { fetchWithAuth } from "@/lib/auth/client";
import type { CommentsResponse, SuccessMessage } from "@/types/api";
export async function listModerationComments(
params: Record<string, string | number | boolean | null | undefined> = {},
) {
return fetchWithAuth<CommentsResponse>(apiEndpoints.comments.moderation(params));
}
export async function deleteAdminComment(commentId: string) {
return fetchWithAuth<SuccessMessage>(apiEndpoints.comments.adminDelete(commentId), {
method: "DELETE",
});
}

عرض الملف

@@ -0,0 +1,36 @@
import type { ApiIdentifier, PaginatedResponse } from "@/types/api";
export function getEntityId(entity?: ApiIdentifier | null) {
if (!entity) return "";
return entity._id ?? entity.id ?? "";
}
export function getItems<T>(payload: PaginatedResponse<T> | T[] | undefined | null) {
if (!payload) return [] as T[];
if (Array.isArray(payload)) return payload;
if (Array.isArray(payload.items)) return payload.items;
if (Array.isArray(payload.data)) return payload.data;
return [] as T[];
}
export function getTotal<T>(payload: PaginatedResponse<T> | undefined | null) {
if (!payload) return 0;
if (typeof payload.pagination?.total === "number") return payload.pagination.total;
if (typeof payload.total === "number") return payload.total;
return getItems(payload).length;
}
export function getPagination<T>(payload: PaginatedResponse<T> | undefined | null) {
return payload?.pagination ?? null;
}
export function toQueryString(params: Record<string, string | number | boolean | null | undefined>) {
const search = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value === undefined || value === null || value === "") return;
search.set(key, String(value));
});
return search.toString();
}

عرض الملف

@@ -0,0 +1,118 @@
import { toQueryString } from "@/lib/api/core";
export const apiEndpoints = {
health: "/",
auth: {
login: "/auth/login",
refresh: "/auth/refresh",
logout: "/auth/logout",
superAdminLogin: "/auth/superadmin/login",
superAdminRefresh: "/auth/superadmin/refresh",
superAdminLogout: "/auth/superadmin/logout",
superAdminSessions: "/auth/superadmin/sessions",
revokeSuperAdminSession: (sessionId: string) => `/auth/superadmin/sessions/${sessionId}/revoke`,
},
users: {
all: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/users/admin?${toQueryString(params)}`,
byId: (userId: string) => `/users/admin/${userId}`,
update: (userId: string) => `/users/admin/${userId}`,
disable: (userId: string) => `/users/admin/${userId}/disable`,
enable: (userId: string) => `/users/admin/${userId}/enable`,
remove: (userId: string) => `/users/admin/${userId}`,
setRole: (userId: string) => `/users/admin/${userId}/role`,
admins: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/users/admin/admins?${toQueryString(params)}`,
updateAdmin: (userId: string) => `/users/admin/admins/${userId}`,
removeAdmin: (userId: string) => `/users/admin/admins/${userId}`,
createAdmin: "/users/admin/create-admin",
discover: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/users/admin/discover?${toQueryString(params)}`,
profileOverview: (userId: string) => `/users/admin/${userId}/profile-overview`,
},
posts: {
moderation: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/posts/admin/moderation?${toQueryString(params)}`,
adminDelete: (postId: string) => `/posts/admin/${postId}`,
},
comments: {
moderation: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/comments/admin?${toQueryString(params)}`,
adminDelete: (commentId: string) => `/comments/admin/${commentId}`,
},
notifications: {
superAdmin: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/notifications/superadmin?${toQueryString(params)}`,
},
reports: {
superAdmin: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/reports/superadmin?${toQueryString(params)}`,
updateStatus: (reportId: string) => `/reports/superadmin/${reportId}/status`,
},
marketplace: {
home: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/marketplace/home?${toQueryString(params)}`,
shopByAdminId: (adminId: string) => `/marketplace/shops/${adminId}`,
adminShopProfile: "/marketplace/admin/shop-profile",
adminMyShopProfile: "/marketplace/admin/shop-profile/me",
adminCreateRepairShop: "/marketplace/admin/repair-shops",
adminUpdateRepairShop: (repairShopId: string) => `/marketplace/admin/repair-shops/${repairShopId}`,
adminDeleteRepairShop: (repairShopId: string) => `/marketplace/admin/repair-shops/${repairShopId}`,
adminMyRepairShops: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/marketplace/admin/repair-shops/me?${toQueryString(params)}`,
adminCreateInstrument: "/marketplace/admin/instruments",
adminUpdateInstrument: (instrumentId: string) => `/marketplace/admin/instruments/${instrumentId}`,
adminDeleteInstrument: (instrumentId: string) => `/marketplace/admin/instruments/${instrumentId}`,
adminMyInstruments: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/marketplace/admin/instruments/me?${toQueryString(params)}`,
adminCreateListing: "/marketplace/admin/listings",
adminUpdateListing: (listingId: string) => `/marketplace/admin/listings/${listingId}`,
adminDeleteListing: (listingId: string) => `/marketplace/admin/listings/${listingId}`,
adminMyListings: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/marketplace/admin/listings/me?${toQueryString(params)}`,
moderationListings: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/marketplace/superadmin/listings?${toQueryString(params)}`,
superAdminCreateListing: (adminId: string) => `/marketplace/superadmin/admins/${adminId}/listings`,
superAdminCreateInstrument: (adminId: string) => `/marketplace/superadmin/admins/${adminId}/instruments`,
superAdminCreateRepairShop: (adminId: string) => `/marketplace/superadmin/admins/${adminId}/repair-shops`,
superAdminUpdateShopProfile: (adminId: string) => `/marketplace/superadmin/admins/${adminId}/shop-profile`,
moderationUpdateListingStatus: (listingId: string) => `/marketplace/superadmin/listings/${listingId}/status`,
moderationDeleteListing: (listingId: string) => `/marketplace/superadmin/listings/${listingId}`,
moderationRepairShops: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/marketplace/superadmin/repair-shops?${toQueryString(params)}`,
moderationUpdateRepairShopStatus: (repairShopId: string) =>
`/marketplace/superadmin/repair-shops/${repairShopId}/status`,
moderationDeleteRepairShop: (repairShopId: string) => `/marketplace/superadmin/repair-shops/${repairShopId}`,
},
audit: {
logs: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/audit/superadmin/logs?${toQueryString(params)}`,
},
superadmin: {
session: "/superadmin/session",
overview: "/superadmin/overview",
charts: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/superadmin/charts?${toQueryString(params)}`,
recentActivity: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/superadmin/recent-activity?${toQueryString(params)}`,
reports: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/superadmin/reports?${toQueryString(params)}`,
ops: "/superadmin/ops",
cases: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/superadmin/cases?${toQueryString(params)}`,
createCase: "/superadmin/cases",
caseById: (caseId: string) => `/superadmin/cases/${caseId}`,
bulkActions: "/superadmin/bulk-actions",
settings: "/superadmin/settings",
settingsHistory: (params: Record<string, string | number | boolean | null | undefined> = {}) =>
`/superadmin/settings/history?${toQueryString(params)}`,
restoreSettingsHistory: (historyId: string) => `/superadmin/settings/history/${historyId}/restore`,
updatePostStatus: (postId: string) => `/superadmin/posts/${postId}/status`,
deletePost: (postId: string) => `/superadmin/posts/${postId}`,
restorePost: (postId: string) => `/superadmin/posts/${postId}/restore`,
updateCommentStatus: (commentId: string) => `/superadmin/comments/${commentId}/status`,
deleteComment: (commentId: string) => `/superadmin/comments/${commentId}`,
restoreComment: (commentId: string) => `/superadmin/comments/${commentId}/restore`,
updateUserStatus: (userId: string) => `/superadmin/users/${userId}/status`,
},
} as const;

عرض الملف

@@ -0,0 +1,6 @@
import { apiEndpoints } from "@/lib/api/endpoints";
import { fetchWithAuth } from "@/lib/auth/client";
export async function getHealth() {
return fetchWithAuth<string | Record<string, unknown>>(apiEndpoints.health);
}

عرض الملف

@@ -0,0 +1,236 @@
import { apiEndpoints } from "@/lib/api/endpoints";
import { fetchWithAuth } from "@/lib/auth/client";
import type {
MarketplaceHomeResponse,
MarketplaceListing,
MarketplaceRepairShop,
MarketplaceRepairShopResponse,
MarketplaceResponse,
MarketplaceShopProfile,
SuccessMessage,
} from "@/types/api";
type MarketplaceFormValue =
| string
| number
| boolean
| File
| null
| undefined
| Array<string | number | boolean | File>;
type MarketplaceFormPayload = Record<string, MarketplaceFormValue>;
function appendFormValue(formData: FormData, key: string, value: MarketplaceFormValue) {
if (value === undefined || value === null || value === "") {
return;
}
if (Array.isArray(value)) {
value.forEach((entry) => appendFormValue(formData, key, entry));
return;
}
if (value instanceof File) {
formData.append(key, value);
return;
}
formData.append(key, String(value));
}
function toMarketplaceFormData(payload: MarketplaceFormPayload) {
const formData = new FormData();
Object.entries(payload).forEach(([key, value]) => appendFormValue(formData, key, value));
return formData;
}
export async function getMarketplaceHome(params: Record<string, string | number | boolean | null | undefined> = {}) {
return fetchWithAuth<MarketplaceHomeResponse>(apiEndpoints.marketplace.home(params));
}
export async function getMarketplaceShopByAdminId(adminId: string) {
return fetchWithAuth<MarketplaceShopProfile>(apiEndpoints.marketplace.shopByAdminId(adminId));
}
export async function getAdminShopProfile() {
return fetchWithAuth<MarketplaceShopProfile>(apiEndpoints.marketplace.adminMyShopProfile);
}
export async function updateAdminShopProfile(payload: MarketplaceFormPayload) {
return fetchWithAuth<MarketplaceShopProfile>(apiEndpoints.marketplace.adminShopProfile, {
method: "PATCH",
body: toMarketplaceFormData(payload),
});
}
export async function createAdminRepairShop(payload: MarketplaceFormPayload) {
return fetchWithAuth<MarketplaceRepairShop>(apiEndpoints.marketplace.adminCreateRepairShop, {
method: "POST",
body: toMarketplaceFormData(payload),
});
}
export async function updateAdminRepairShop(repairShopId: string, payload: MarketplaceFormPayload) {
return fetchWithAuth<MarketplaceRepairShop>(apiEndpoints.marketplace.adminUpdateRepairShop(repairShopId), {
method: "PATCH",
body: toMarketplaceFormData(payload),
});
}
export async function deleteAdminRepairShop(repairShopId: string) {
return fetchWithAuth<SuccessMessage>(apiEndpoints.marketplace.adminDeleteRepairShop(repairShopId), {
method: "DELETE",
});
}
export async function listAdminRepairShops(
params: Record<string, string | number | boolean | null | undefined> = {},
) {
return fetchWithAuth<MarketplaceRepairShopResponse>(apiEndpoints.marketplace.adminMyRepairShops(params));
}
export async function createAdminInstrument(payload: MarketplaceFormPayload) {
return fetchWithAuth<MarketplaceListing>(apiEndpoints.marketplace.adminCreateInstrument, {
method: "POST",
body: toMarketplaceFormData(payload),
});
}
export async function updateAdminInstrument(instrumentId: string, payload: MarketplaceFormPayload) {
return fetchWithAuth<MarketplaceListing>(apiEndpoints.marketplace.adminUpdateInstrument(instrumentId), {
method: "PATCH",
body: toMarketplaceFormData(payload),
});
}
export async function deleteAdminInstrument(instrumentId: string) {
return fetchWithAuth<SuccessMessage>(apiEndpoints.marketplace.adminDeleteInstrument(instrumentId), {
method: "DELETE",
});
}
export async function listAdminInstruments(
params: Record<string, string | number | boolean | null | undefined> = {},
) {
return fetchWithAuth<MarketplaceResponse>(apiEndpoints.marketplace.adminMyInstruments(params));
}
export async function createAdminListing(payload: MarketplaceFormPayload) {
return fetchWithAuth<MarketplaceListing>(apiEndpoints.marketplace.adminCreateListing, {
method: "POST",
body: toMarketplaceFormData(payload),
});
}
export async function updateAdminListing(listingId: string, payload: MarketplaceFormPayload) {
return fetchWithAuth<MarketplaceListing>(apiEndpoints.marketplace.adminUpdateListing(listingId), {
method: "PATCH",
body: toMarketplaceFormData(payload),
});
}
export async function deleteAdminListing(listingId: string) {
return fetchWithAuth<SuccessMessage>(apiEndpoints.marketplace.adminDeleteListing(listingId), {
method: "DELETE",
});
}
export async function listAdminListings(
params: Record<string, string | number | boolean | null | undefined> = {},
) {
return fetchWithAuth<MarketplaceResponse>(apiEndpoints.marketplace.adminMyListings(params));
}
export async function listModerationListings(params: Record<string, string | number | boolean | null | undefined> = {}) {
return fetchWithAuth<MarketplaceResponse>(apiEndpoints.marketplace.moderationListings(params));
}
export async function updateModerationListingStatus(
listingId: string,
isActive: boolean,
reason?: string,
) {
return fetchWithAuth<MarketplaceListing>(apiEndpoints.marketplace.moderationUpdateListingStatus(listingId), {
method: "PATCH",
body: JSON.stringify({ isActive, reason }),
});
}
export async function deleteModerationListing(listingId: string) {
return fetchWithAuth<SuccessMessage>(apiEndpoints.marketplace.moderationDeleteListing(listingId), {
method: "DELETE",
});
}
export async function listModerationRepairShops(
params: Record<string, string | number | boolean | null | undefined> = {},
) {
return fetchWithAuth<MarketplaceRepairShopResponse>(apiEndpoints.marketplace.moderationRepairShops(params));
}
export async function updateModerationRepairShopStatus(
repairShopId: string,
isActive: boolean,
reason?: string,
) {
return fetchWithAuth<MarketplaceRepairShop>(
apiEndpoints.marketplace.moderationUpdateRepairShopStatus(repairShopId),
{
method: "PATCH",
body: JSON.stringify({ isActive, reason }),
},
);
}
export async function deleteModerationRepairShop(repairShopId: string) {
return fetchWithAuth<SuccessMessage>(apiEndpoints.marketplace.moderationDeleteRepairShop(repairShopId), {
method: "DELETE",
});
}
export async function createMarketplaceListingForSuperAdmin(
adminId: string,
payload: MarketplaceFormPayload,
) {
return fetchWithAuth<MarketplaceListing>(apiEndpoints.marketplace.superAdminCreateListing(adminId), {
method: "POST",
body: toMarketplaceFormData(payload),
});
}
export async function createMarketplaceInstrumentForSuperAdmin(
adminId: string,
payload: MarketplaceFormPayload,
) {
return fetchWithAuth<MarketplaceListing>(apiEndpoints.marketplace.superAdminCreateInstrument(adminId), {
method: "POST",
body: toMarketplaceFormData(payload),
});
}
export async function createMarketplaceRepairShopForSuperAdmin(
adminId: string,
payload: MarketplaceFormPayload,
) {
return fetchWithAuth<MarketplaceRepairShop>(
apiEndpoints.marketplace.superAdminCreateRepairShop(adminId),
{
method: "POST",
body: toMarketplaceFormData(payload),
},
);
}
export async function updateMarketplaceShopProfileForSuperAdmin(
adminId: string,
payload: MarketplaceFormPayload,
) {
return fetchWithAuth<MarketplaceShopProfile>(
apiEndpoints.marketplace.superAdminUpdateShopProfile(adminId),
{
method: "PATCH",
body: toMarketplaceFormData(payload),
},
);
}

عرض الملف

@@ -0,0 +1,9 @@
import { apiEndpoints } from "@/lib/api/endpoints";
import { fetchWithAuth } from "@/lib/auth/client";
import type { NotificationsResponse } from "@/types/api";
export async function listPlatformNotifications(
params: Record<string, string | number | boolean | null | undefined> = {},
) {
return fetchWithAuth<NotificationsResponse>(apiEndpoints.notifications.superAdmin(params));
}

عرض الملف

@@ -0,0 +1,13 @@
import { apiEndpoints } from "@/lib/api/endpoints";
import { fetchWithAuth } from "@/lib/auth/client";
import type { PostsResponse, SuccessMessage } from "@/types/api";
export async function listModerationPosts(params: Record<string, string | number | boolean | null | undefined> = {}) {
return fetchWithAuth<PostsResponse>(apiEndpoints.posts.moderation(params));
}
export async function deleteAdminPost(postId: string) {
return fetchWithAuth<SuccessMessage>(apiEndpoints.posts.adminDelete(postId), {
method: "DELETE",
});
}

عرض الملف

@@ -0,0 +1,20 @@
import { apiEndpoints } from "@/lib/api/endpoints";
import { fetchWithAuth } from "@/lib/auth/client";
import type { PlatformReport, ReportsResponse, ReportStatus } from "@/types/api";
export async function listPlatformReports(
params: Record<string, string | number | boolean | null | undefined> = {},
) {
return fetchWithAuth<ReportsResponse>(apiEndpoints.reports.superAdmin(params));
}
export async function updatePlatformReportStatus(
reportId: string,
status: ReportStatus,
resolutionNote?: string,
) {
return fetchWithAuth<PlatformReport>(apiEndpoints.reports.updateStatus(reportId), {
method: "PATCH",
body: JSON.stringify({ status, resolutionNote }),
});
}

عرض الملف

@@ -0,0 +1,182 @@
import { apiEndpoints } from "@/lib/api/endpoints";
import { fetchWithAuth } from "@/lib/auth/client";
import type {
ApiComment,
ApiPost,
ApiUser,
CreateSuperAdminCasePayload,
ModerationStatus,
SuperAdminCase,
SuperAdminCasesResponse,
SuperAdminChartsResponse,
SuperAdminOpsResponse,
SuperAdminOverviewResponse,
SuperAdminRecentActivityResponse,
SuperAdminReportsResponse,
SuperAdminSessionResponse,
SuperAdminSettings,
SuperAdminSettingsHistoryResponse,
SuperAdminSettingsResponse,
} from "@/types/api";
export async function getSuperAdminSession() {
return fetchWithAuth<SuperAdminSessionResponse>(apiEndpoints.superadmin.session);
}
export async function getSuperAdminOverview() {
return fetchWithAuth<SuperAdminOverviewResponse>(apiEndpoints.superadmin.overview);
}
export async function getSuperAdminCharts(
params: Record<string, string | number | boolean | null | undefined> = {},
) {
return fetchWithAuth<SuperAdminChartsResponse>(apiEndpoints.superadmin.charts(params));
}
export async function getSuperAdminRecentActivity(
params: Record<string, string | number | boolean | null | undefined> = {},
) {
return fetchWithAuth<SuperAdminRecentActivityResponse>(apiEndpoints.superadmin.recentActivity(params));
}
export async function getSuperAdminReports(
params: Record<string, string | number | boolean | null | undefined> = {},
) {
return fetchWithAuth<SuperAdminReportsResponse>(apiEndpoints.superadmin.reports(params));
}
export async function getSuperAdminOps() {
return fetchWithAuth<SuperAdminOpsResponse>(apiEndpoints.superadmin.ops);
}
export async function getSuperAdminCases(
params: Record<string, string | number | boolean | null | undefined> = {},
) {
return fetchWithAuth<SuperAdminCasesResponse>(apiEndpoints.superadmin.cases(params));
}
export async function createSuperAdminCase(payload: CreateSuperAdminCasePayload) {
return fetchWithAuth<SuperAdminCase>(apiEndpoints.superadmin.createCase, {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function updateSuperAdminCase(
caseId: string,
payload: Partial<SuperAdminCase> & { note?: string },
) {
return fetchWithAuth<SuperAdminCase>(apiEndpoints.superadmin.caseById(caseId), {
method: "PATCH",
body: JSON.stringify(payload),
});
}
export async function performSuperAdminBulkAction(payload: {
resourceType: string;
targetIds: string[];
action: string;
reason?: string;
priority?: string;
assignToMe?: boolean;
}) {
return fetchWithAuth<Record<string, unknown>>(apiEndpoints.superadmin.bulkActions, {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function getSuperAdminSettings() {
return fetchWithAuth<SuperAdminSettingsResponse>(apiEndpoints.superadmin.settings);
}
export async function getSuperAdminSettingsHistory(
params: Record<string, string | number | boolean | null | undefined> = {},
) {
return fetchWithAuth<SuperAdminSettingsHistoryResponse>(
apiEndpoints.superadmin.settingsHistory(params),
);
}
export async function updateSuperAdminSettings(payload: Partial<SuperAdminSettings>) {
return fetchWithAuth<SuperAdminSettingsResponse>(apiEndpoints.superadmin.settings, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
export async function restoreSuperAdminSettingsHistory(historyId: string) {
return fetchWithAuth<SuperAdminSettingsResponse>(
apiEndpoints.superadmin.restoreSettingsHistory(historyId),
{
method: "POST",
},
);
}
export async function updateSuperAdminPostStatus(postId: string, status: ModerationStatus, reason?: string) {
return fetchWithAuth<ApiPost>(apiEndpoints.superadmin.updatePostStatus(postId), {
method: "PATCH",
body: JSON.stringify({ status, reason }),
});
}
export async function deleteSuperAdminPost(postId: string) {
return fetchWithAuth<Record<string, unknown>>(apiEndpoints.superadmin.deletePost(postId), {
method: "DELETE",
});
}
export async function restoreSuperAdminPost(postId: string) {
return fetchWithAuth<ApiPost>(apiEndpoints.superadmin.restorePost(postId), {
method: "POST",
});
}
export async function updateSuperAdminCommentStatus(
commentId: string,
status: ModerationStatus,
reason?: string,
) {
return fetchWithAuth<ApiComment>(apiEndpoints.superadmin.updateCommentStatus(commentId), {
method: "PATCH",
body: JSON.stringify({ status, reason }),
});
}
export async function deleteSuperAdminComment(commentId: string) {
return fetchWithAuth<Record<string, unknown>>(apiEndpoints.superadmin.deleteComment(commentId), {
method: "DELETE",
});
}
export async function restoreSuperAdminComment(commentId: string) {
return fetchWithAuth<ApiComment>(apiEndpoints.superadmin.restoreComment(commentId), {
method: "POST",
});
}
export async function updateSuperAdminUserStatus(userId: string, isDisabled: boolean, reason?: string) {
try {
return await fetchWithAuth<ApiUser>(apiEndpoints.superadmin.updateUserStatus(userId), {
method: "PATCH",
body: JSON.stringify({ isDisabled, reason }),
});
} catch (error) {
const message = String(error);
if (!message.includes("404")) {
throw error;
}
if (isDisabled) {
return fetchWithAuth<ApiUser>(apiEndpoints.users.disable(userId), {
method: "PATCH",
body: JSON.stringify({ reason: reason ?? "Disabled by SuperAdmin dashboard" }),
});
}
return fetchWithAuth<ApiUser>(apiEndpoints.users.enable(userId), {
method: "PATCH",
});
}
}

عرض الملف

@@ -0,0 +1,108 @@
import { apiEndpoints } from "@/lib/api/endpoints";
import type { LoginResponse } from "@/types/api";
const API_PREFIX = "/api/proxy";
function isHardAuthFailure(error: unknown) {
const message = String(error);
return message.includes("400") || message.includes("401");
}
function extractErrorMessage(payload: unknown): string | null {
if (!payload || typeof payload !== "object") {
return null;
}
const candidate = payload as { message?: unknown; error?: unknown };
if (Array.isArray(candidate.message)) {
return candidate.message.map((item) => String(item)).join(", ");
}
if (typeof candidate.message === "string" && candidate.message.trim()) {
return candidate.message.trim();
}
if (typeof candidate.error === "string" && candidate.error.trim()) {
return candidate.error.trim();
}
return null;
}
async function request<T>(path: string, init?: RequestInit): Promise<T> {
const isFormDataRequest =
typeof FormData !== "undefined" && init?.body instanceof FormData;
const res = await fetch(`${API_PREFIX}${path}`, {
...init,
credentials: "include",
headers: {
...(isFormDataRequest ? {} : { "Content-Type": "application/json" }),
...(init?.headers ?? {}),
},
});
if (!res.ok) {
const contentType = res.headers.get("content-type") ?? "";
let detail = "";
if (contentType.includes("application/json")) {
const payload = (await res.json()) as unknown;
const message = extractErrorMessage(payload);
detail = message ? ` - ${message}` : ` - ${JSON.stringify(payload)}`;
} else {
const text = await res.text();
detail = text ? ` - ${text}` : "";
}
throw new Error(`Request failed: ${res.status}${detail}`);
}
if (res.status === 204) {
return undefined as T;
}
return res.json() as Promise<T>;
}
export async function loginSuperAdmin(email: string, password: string): Promise<void> {
await request<LoginResponse>(apiEndpoints.auth.superAdminLogin, {
method: "POST",
body: JSON.stringify({ email, password }),
});
}
export async function refreshSuperAdmin(): Promise<boolean> {
try {
await request<LoginResponse>(apiEndpoints.auth.superAdminRefresh, {
method: "POST",
body: JSON.stringify({}),
});
return true;
} catch (error) {
if (isHardAuthFailure(error)) {
return false;
}
throw error;
}
}
export async function logoutSuperAdmin(): Promise<void> {
await request<void>(apiEndpoints.auth.superAdminLogout, {
method: "POST",
body: JSON.stringify({}),
});
}
export async function fetchWithAuth<T>(path: string, init?: RequestInit): Promise<T> {
const makeRequest = async () => request<T>(path, init);
try {
return await makeRequest();
} catch (error) {
if (String(error).includes("401")) {
const refreshed = await refreshSuperAdmin();
if (refreshed) {
return makeRequest();
}
}
throw error;
}
}

عرض الملف

@@ -0,0 +1,23 @@
export function parseJwtPayload(token: string): Record<string, unknown> | null {
const parts = token.split(".");
if (parts.length < 2) return null;
try {
const normalized = parts[1].replace(/-/g, "+").replace(/_/g, "/");
const decoded =
typeof window !== "undefined"
? window.atob(normalized)
: Buffer.from(normalized, "base64").toString("utf8");
return JSON.parse(decoded) as Record<string, unknown>;
} catch {
return null;
}
}
export function isJwtExpired(token: string, skewSeconds = 15): boolean {
const payload = parseJwtPayload(token);
const exp = typeof payload?.exp === "number" ? payload.exp : null;
if (!exp) return false;
const now = Math.floor(Date.now() / 1000);
return exp <= now + skewSeconds;
}

عرض الملف

@@ -0,0 +1,14 @@
import type { AuthTokens } from "@/types/api";
export function loadTokens(): AuthTokens | null {
return null;
}
export function saveTokens(tokens: AuthTokens) {
void tokens;
return;
}
export function clearTokens() {
return;
}

عرض الملف

@@ -0,0 +1,28 @@
export function formatDateTime(value?: string | null) {
if (!value) return "-";
try {
return new Intl.DateTimeFormat("ar-SA", {
dateStyle: "medium",
timeStyle: "short",
}).format(new Date(value));
} catch {
return value;
}
}
export function formatCurrency(value?: number | null, currency = "SAR") {
if (typeof value !== "number") return "-";
try {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency,
maximumFractionDigits: 0,
}).format(value);
} catch {
return `${value} ${currency}`;
}
}
export function formatCount(value?: number | null) {
return typeof value === "number" ? new Intl.NumberFormat("en-US").format(value) : "0";
}

عرض الملف

@@ -0,0 +1,37 @@
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL ?? process.env.API_BASE_URL ?? "";
function getApiOrigin() {
try {
return new URL(API_BASE_URL).origin;
} catch {
return "";
}
}
export function resolveMediaUrl(value?: string | null) {
const url = value?.trim() ?? "";
if (!url) {
return "";
}
if (
/^[a-z][a-z0-9+.-]*:\/\//i.test(url) ||
url.startsWith("//") ||
url.startsWith("data:") ||
url.startsWith("blob:")
) {
return url;
}
const origin = getApiOrigin();
if (!origin) {
return url;
}
if (url.startsWith("/")) {
return `${origin}${url}`;
}
return `${origin}/${url.replace(/^\/+/, "")}`;
}

عرض الملف

@@ -0,0 +1,34 @@
import type { Insight, SettingsNotifications, SettingsProfile } from "@/types";
export const retentionCurve: Insight[] = [
{ label: "الأسبوع 1", value: 100 },
{ label: "الأسبوع 2", value: 84 },
{ label: "الأسبوع 3", value: 73 },
{ label: "الأسبوع 4", value: 64 },
{ label: "الأسبوع 5", value: 58 },
{ label: "الأسبوع 6", value: 53 },
];
export const cityPerformance: Insight[] = [
{ label: "الرياض", value: 34 },
{ label: "جدة", value: 22 },
{ label: "الدمام", value: 15 },
{ label: "المدينة", value: 11 },
{ label: "مكة", value: 9 },
{ label: "أخرى", value: 9 },
];
export const profileDefaults: SettingsProfile = {
businessName: "Oudelaa Music Experience",
supportEmail: "support@oudelaa.com",
supportPhone: "+966 50 111 2233",
city: "Riyadh",
currency: "SAR",
};
export const notificationDefaults: SettingsNotifications = {
orders: true,
newUsers: true,
payouts: false,
weeklyDigest: true,
};

عرض الملف

@@ -0,0 +1,9 @@
import type { ContentItem } from "@/types";
export const contentLibrary: ContentItem[] = [
{ id: "CNT-201", title: "موال السهرة", artist: "فرقة وتر الحجاز", genre: "طرب", duration: "06:12", status: "published", createdAt: "2026-03-10", listens: 12890 },
{ id: "CNT-202", title: "ليالي العود", artist: "نواف الدوسري", genre: "Instrumental", duration: "04:48", status: "review", createdAt: "2026-03-26", listens: 7420 },
{ id: "CNT-203", title: "نبض المرسى", artist: "سدن", genre: "Fusion", duration: "03:55", status: "draft", createdAt: "2026-04-01", listens: 0 },
{ id: "CNT-204", title: "رحلة مقام", artist: "أمجاد", genre: "Classical", duration: "05:24", status: "published", createdAt: "2026-02-15", listens: 15430 },
{ id: "CNT-205", title: "نخل الجنوب", artist: "علي الشهري", genre: "Folk", duration: "04:02", status: "published", createdAt: "2026-03-29", listens: 5120 },
];

عرض الملف

@@ -0,0 +1,36 @@
import type { ChannelPoint, RevenuePoint, StatMetric } from "@/types";
export const dashboardStats: StatMetric[] = [
{ id: "gmv", label: "إجمالي الإيراد", value: "1,284,920 ر.س", delta: "+18.6%", trend: "up", note: "مقارنة بـ مارس" },
{ id: "orders", label: "الطلبات المكتملة", value: "4,912", delta: "+11.2%", trend: "up", note: "آخر 30 يوم" },
{ id: "aov", label: "متوسط قيمة الطلب", value: "261 ر.س", delta: "-2.4%", trend: "down", note: "يحتاج تحسين سلة الشراء" },
{ id: "retention", label: "الاحتفاظ بالعملاء", value: "82.1%", delta: "+3.1%", trend: "up", note: "العملاء العائدون" },
];
export const revenueSeries: RevenuePoint[] = [
{ month: "يناير", revenue: 780000, orders: 2900 },
{ month: "فبراير", revenue: 842000, orders: 3180 },
{ month: "مارس", revenue: 906000, orders: 3375 },
{ month: "أبريل", revenue: 1002000, orders: 3710 },
{ month: "مايو", revenue: 1093000, orders: 3980 },
{ month: "يونيو", revenue: 1160000, orders: 4250 },
{ month: "يوليو", revenue: 1284920, orders: 4912 },
];
export const channelShare: ChannelPoint[] = [
{ name: "التطبيق", value: 46 },
{ name: "الموقع", value: 33 },
{ name: "واتساب", value: 14 },
{ name: "شركاء", value: 7 },
];
export const heatmapHours = [
{ slot: "09:00", score: 20 },
{ slot: "11:00", score: 42 },
{ slot: "13:00", score: 54 },
{ slot: "15:00", score: 79 },
{ slot: "17:00", score: 92 },
{ slot: "19:00", score: 68 },
{ slot: "21:00", score: 49 },
{ slot: "23:00", score: 30 },
];

عرض الملف

@@ -0,0 +1,6 @@
export * from "./dashboard";
export * from "./users";
export * from "./content";
export * from "./orders";
export * from "./messages";
export * from "./analytics";

عرض الملف

@@ -0,0 +1,8 @@
import type { MessageThread } from "@/types";
export const messageThreads: MessageThread[] = [
{ id: "MSG-881", customer: "رؤى السالم", subject: "تحديث الاشتراك", state: "unread", lastMessageAt: "2026-04-06 11:03", channel: "WhatsApp", preview: "أحتاج ترقية من Silver إلى Gold مع حفظ المكتبة الحالية.", priority: "high" },
{ id: "MSG-882", customer: "حسين العمري", subject: "مشكلة في الدفع", state: "open", lastMessageAt: "2026-04-06 09:54", channel: "Email", preview: "تم خصم المبلغ ولم يظهر الطلب في لوحة الطلبات.", priority: "high" },
{ id: "MSG-883", customer: "سارة البقمي", subject: "طلب تعاون فني", state: "open", lastMessageAt: "2026-04-05 23:15", channel: "In-App", preview: "هل يمكن إدراج أعمالي ضمن قسم المواهب الجديدة؟", priority: "normal" },
{ id: "MSG-884", customer: "عبدالاله الدوسري", subject: "استفسار باقة Maestro", state: "closed", lastMessageAt: "2026-04-04 20:01", channel: "Email", preview: "تم الرد وإغلاق التذكرة مع مشاركة العرض السعري.", priority: "low" },
];

عرض الملف

@@ -0,0 +1,10 @@
import type { Order } from "@/types";
export const orders: Order[] = [
{ id: "ORD-7781", customer: "مشاعل الكندري", product: "باقة Oudelaa Gold", amount: 420, paymentMethod: "Card", status: "paid", createdAt: "2026-04-06 10:42", city: "الرياض" },
{ id: "ORD-7780", customer: "فهد العلي", product: "جلسة Maestro Live", amount: 960, paymentMethod: "Apple Pay", status: "processing", createdAt: "2026-04-06 09:27", city: "جدة" },
{ id: "ORD-7779", customer: "أروى الحازمي", product: "باقة Silver", amount: 190, paymentMethod: "Wallet", status: "paid", createdAt: "2026-04-06 08:15", city: "الخبر" },
{ id: "ORD-7778", customer: "محمد اليامي", product: "إهداء مقطوعة", amount: 300, paymentMethod: "Card", status: "refunded", createdAt: "2026-04-05 22:50", city: "أبها" },
{ id: "ORD-7777", customer: "غلا السبيعي", product: "اشتراك سنوي", amount: 1880, paymentMethod: "Card", status: "failed", createdAt: "2026-04-05 20:33", city: "الدمام" },
{ id: "ORD-7776", customer: "بدر الجهني", product: "باقة Gold", amount: 520, paymentMethod: "Apple Pay", status: "processing", createdAt: "2026-04-05 18:02", city: "الرياض" },
];

عرض الملف

@@ -0,0 +1,10 @@
import type { User } from "@/types";
export const users: User[] = [
{ id: "USR-1048", fullName: "ريم الحربي", email: "reem@oudelaa.sa", plan: "Maestro", status: "active", joinedAt: "2026-02-13", totalSpent: 12680, city: "الرياض" },
{ id: "USR-1049", fullName: "سلمان العتيبي", email: "salman@oudelaa.sa", plan: "Gold", status: "active", joinedAt: "2026-01-29", totalSpent: 7480, city: "جدة" },
{ id: "USR-1050", fullName: "نور البلوشي", email: "nour@oudelaa.sa", plan: "Silver", status: "pending", joinedAt: "2026-03-03", totalSpent: 980, city: "الدمام" },
{ id: "USR-1051", fullName: "عبدالرحمن الزهراني", email: "rahman@oudelaa.sa", plan: "Gold", status: "suspended", joinedAt: "2025-11-17", totalSpent: 5320, city: "الخبر" },
{ id: "USR-1052", fullName: "لولوة المطيري", email: "lolwah@oudelaa.sa", plan: "Maestro", status: "active", joinedAt: "2026-03-19", totalSpent: 15400, city: "المدينة" },
{ id: "USR-1053", fullName: "زياد القحطاني", email: "ziad@oudelaa.sa", plan: "Silver", status: "active", joinedAt: "2026-02-22", totalSpent: 2280, city: "الطائف" },
];

عرض الملف

@@ -0,0 +1,107 @@
import {
Bell,
ChartColumn,
Flag,
LayoutDashboard,
MessageSquareMore,
PackageSearch,
Settings,
ShieldCheck,
ShoppingBag,
SquareKanban,
Users,
type LucideIcon,
} from "lucide-react";
import type { PermissionMatchMode } from "@/lib/permissions";
import { SUPERADMIN_PERMISSIONS } from "@/lib/permissions";
export type DashboardNavItem = {
href: string;
label: string;
icon: LucideIcon;
permissionMode?: PermissionMatchMode;
requiredPermissions: readonly string[];
};
export const dashboardNav = [
{
href: "/dashboard",
label: "Dashboard",
icon: LayoutDashboard,
requiredPermissions: [SUPERADMIN_PERMISSIONS.OVERVIEW_READ],
},
{
href: "/users",
label: "Users",
icon: Users,
requiredPermissions: [SUPERADMIN_PERMISSIONS.USERS_READ],
},
{
href: "/analytics",
label: "Analytics",
icon: ChartColumn,
requiredPermissions: [SUPERADMIN_PERMISSIONS.ANALYTICS_READ],
},
{
href: "/content",
label: "Content",
icon: SquareKanban,
requiredPermissions: [SUPERADMIN_PERMISSIONS.CONTENT_MODERATE],
},
{
href: "/reports",
label: "Reports",
icon: Flag,
requiredPermissions: [SUPERADMIN_PERMISSIONS.CONTENT_MODERATE],
},
{
href: "/marketplace",
label: "Marketplace",
icon: ShoppingBag,
requiredPermissions: [SUPERADMIN_PERMISSIONS.MARKETPLACE_MANAGE],
},
{
href: "/notifications",
label: "Notifications",
icon: Bell,
requiredPermissions: [SUPERADMIN_PERMISSIONS.NOTIFICATIONS_READ],
},
{
href: "/messages",
label: "Engagement",
icon: MessageSquareMore,
permissionMode: "any",
requiredPermissions: [
SUPERADMIN_PERMISSIONS.NOTIFICATIONS_READ,
SUPERADMIN_PERMISSIONS.CONTENT_MODERATE,
],
},
{
href: "/orders",
label: "Operations",
icon: PackageSearch,
permissionMode: "any",
requiredPermissions: [
SUPERADMIN_PERMISSIONS.CASES_MANAGE,
SUPERADMIN_PERMISSIONS.OPS_READ,
],
},
{
href: "/security",
label: "Security",
icon: ShieldCheck,
permissionMode: "any",
requiredPermissions: [
SUPERADMIN_PERMISSIONS.SESSIONS_MANAGE,
SUPERADMIN_PERMISSIONS.AUDIT_READ,
SUPERADMIN_PERMISSIONS.OPS_READ,
],
},
{
href: "/settings",
label: "Settings",
icon: Settings,
requiredPermissions: [SUPERADMIN_PERMISSIONS.SETTINGS_READ],
},
] satisfies readonly DashboardNavItem[];

عرض الملف

@@ -0,0 +1,37 @@
export const SUPERADMIN_PERMISSIONS = {
OVERVIEW_READ: "overview.read",
ANALYTICS_READ: "analytics.read",
USERS_READ: "users.read",
USERS_MANAGE: "users.manage",
CONTENT_MODERATE: "content.moderate",
MARKETPLACE_MANAGE: "marketplace.manage",
NOTIFICATIONS_READ: "notifications.read",
AUDIT_READ: "audit.read",
SETTINGS_READ: "settings.read",
SETTINGS_WRITE: "settings.write",
SESSIONS_MANAGE: "sessions.manage",
OPS_READ: "ops.read",
CASES_MANAGE: "cases.manage",
} as const;
export type PermissionMatchMode = "all" | "any";
export function hasPermission(permissions: string[] | undefined, permission: string) {
return (permissions ?? []).includes(permission);
}
export function matchesPermissions(
permissions: string[] | undefined,
requiredPermissions: readonly string[],
mode: PermissionMatchMode = "all",
) {
if (!requiredPermissions.length) {
return true;
}
if (mode === "any") {
return requiredPermissions.some((permission) => hasPermission(permissions, permission));
}
return requiredPermissions.every((permission) => hasPermission(permissions, permission));
}

عرض الملف

@@ -0,0 +1,73 @@
import { resolveMediaUrl } from "@/lib/media-url";
import type { ApiComment, ApiPost, ApiUser } from "@/types/api";
function asUser(value: ApiUser | string | undefined | null) {
if (!value || typeof value === "string") {
return null;
}
return value;
}
export function getPostAuthor(post: ApiPost) {
return asUser(post.author ?? post.authorId);
}
export function getCommentAuthor(comment: ApiComment) {
return asUser(comment.author ?? comment.authorId);
}
export function getUserLabel(user: ApiUser | null) {
if (!user) {
return "-";
}
return user.name ?? user.stageName ?? user.username ?? user.email ?? "-";
}
export function getPostPreviewMedia(post: ApiPost) {
const imageCount = post.imageUrls?.length ?? 0;
const imageUrl = resolveMediaUrl(post.imageUrls?.[0]);
const thumbnailUrl = resolveMediaUrl(post.thumbnailUrl);
const videoUrl = resolveMediaUrl(post.videoUrl);
const audioUrl = resolveMediaUrl(post.audioUrl);
if (imageCount > 0 && imageUrl) {
return {
kind: "image" as const,
url: imageUrl,
sourceUrl: "",
count: imageCount,
};
}
if (thumbnailUrl) {
return {
kind: post.postType === "audio" ? ("audio" as const) : ("video" as const),
url: thumbnailUrl,
sourceUrl: post.postType === "video" ? videoUrl : audioUrl,
count: 1,
};
}
if (post.postType === "video" && videoUrl) {
return {
kind: "video" as const,
url: "",
sourceUrl: videoUrl,
count: 1,
};
}
return {
kind:
post.postType === "audio"
? ("audio" as const)
: post.postType === "video"
? ("video" as const)
: ("text" as const),
url: "",
sourceUrl: post.postType === "video" ? videoUrl : post.postType === "audio" ? audioUrl : "",
count: 0,
};
}

عرض الملف

@@ -0,0 +1,6 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}