feat: implement core admin dashboard infrastructure including service layers, UI components, and submission management pages
This commit is contained in:
@@ -31,7 +31,7 @@ export default function Profile() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03] lg:p-6">
|
<div className="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/3 lg:p-6">
|
||||||
<h3 className="mb-5 text-lg font-semibold text-gray-800 dark:text-white/90 lg:mb-7">
|
<h3 className="mb-5 text-lg font-semibold text-gray-800 dark:text-white/90 lg:mb-7">
|
||||||
Profile
|
Profile
|
||||||
</h3>
|
</h3>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function BlankPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageBreadcrumb pageTitle="Blank Page" />
|
<PageBreadcrumb pageTitle="Blank Page" />
|
||||||
<div className="min-h-screen rounded-2xl border border-gray-200 bg-white px-5 py-7 dark:border-gray-800 dark:bg-white/[0.03] xl:px-10 xl:py-12">
|
<div className="min-h-screen rounded-2xl border border-gray-200 bg-white px-5 py-7 dark:border-gray-800 dark:bg-white/3 xl:px-10 xl:py-12">
|
||||||
<div className="mx-auto w-full max-w-[630px] text-center">
|
<div className="mx-auto w-full max-w-[630px] text-center">
|
||||||
<h3 className="mb-4 font-semibold text-gray-800 text-theme-xl dark:text-white/90 sm:text-2xl">
|
<h3 className="mb-4 font-semibold text-gray-800 text-theme-xl dark:text-white/90 sm:text-2xl">
|
||||||
Card Title Here
|
Card Title Here
|
||||||
|
|||||||
@@ -240,7 +240,6 @@ export default function Page() {
|
|||||||
} else {
|
} else {
|
||||||
toast.error("Lỗi hệ thống khi cập nhật");
|
toast.error("Lỗi hệ thống khi cập nhật");
|
||||||
}
|
}
|
||||||
console.error(error);
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default function BadgePage() {
|
|||||||
<div>
|
<div>
|
||||||
<PageBreadcrumb pageTitle="Badges" />
|
<PageBreadcrumb pageTitle="Badges" />
|
||||||
<div className="space-y-5 sm:space-y-6">
|
<div className="space-y-5 sm:space-y-6">
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]">
|
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/3">
|
||||||
<div className="px-6 py-5">
|
<div className="px-6 py-5">
|
||||||
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
|
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
|
||||||
With Light Background
|
With Light Background
|
||||||
@@ -50,7 +50,7 @@ export default function BadgePage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]">
|
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/3">
|
||||||
<div className="px-6 py-5">
|
<div className="px-6 py-5">
|
||||||
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
|
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
|
||||||
With Solid Background
|
With Solid Background
|
||||||
@@ -84,7 +84,7 @@ export default function BadgePage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]">
|
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/3">
|
||||||
<div className="px-6 py-5">
|
<div className="px-6 py-5">
|
||||||
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
|
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
|
||||||
Light Background with Left Icon
|
Light Background with Left Icon
|
||||||
@@ -117,7 +117,7 @@ export default function BadgePage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]">
|
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/3">
|
||||||
<div className="px-6 py-5">
|
<div className="px-6 py-5">
|
||||||
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
|
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
|
||||||
Solid Background with Left Icon
|
Solid Background with Left Icon
|
||||||
@@ -150,7 +150,7 @@ export default function BadgePage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]">
|
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/3">
|
||||||
<div className="px-6 py-5">
|
<div className="px-6 py-5">
|
||||||
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
|
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
|
||||||
Light Background with Right Icon
|
Light Background with Right Icon
|
||||||
@@ -183,7 +183,7 @@ export default function BadgePage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]">
|
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/3">
|
||||||
<div className="px-6 py-5">
|
<div className="px-6 py-5">
|
||||||
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
|
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
|
||||||
Solid Background with Right Icon
|
Solid Background with Right Icon
|
||||||
|
|||||||
+1
-1
@@ -554,7 +554,7 @@ span.flatpickr-weekday,
|
|||||||
@apply p-2;
|
@apply p-2;
|
||||||
}
|
}
|
||||||
.fc .fc-daygrid-day.fc-day-today .fc-scrollgrid-sync-inner {
|
.fc .fc-daygrid-day.fc-day-today .fc-scrollgrid-sync-inner {
|
||||||
@apply rounded-sm bg-gray-100 dark:bg-white/[0.03];
|
@apply rounded-sm bg-gray-100 dark:bg-white/3;
|
||||||
}
|
}
|
||||||
.fc .fc-daygrid-day-number {
|
.fc .fc-daygrid-day-number {
|
||||||
@apply !p-3 text-sm font-medium text-gray-700 dark:text-gray-400;
|
@apply !p-3 text-sm font-medium text-gray-700 dark:text-gray-400;
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ const Calendar: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]">
|
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/3">
|
||||||
<div className="custom-calendar">
|
<div className="custom-calendar">
|
||||||
<FullCalendar
|
<FullCalendar
|
||||||
ref={calendarRef}
|
ref={calendarRef}
|
||||||
@@ -201,8 +201,7 @@ const Calendar: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
<span className="flex items-center justify-center w-5 h-5 mr-2 border border-gray-300 rounded-full box dark:border-gray-700">
|
<span className="flex items-center justify-center w-5 h-5 mr-2 border border-gray-300 rounded-full box dark:border-gray-700">
|
||||||
<span
|
<span
|
||||||
className={`h-2 w-2 rounded-full bg-white ${
|
className={`h-2 w-2 rounded-full bg-white ${eventLevel === key ? "block" : "hidden"
|
||||||
eventLevel === key ? "block" : "hidden"
|
|
||||||
}`}
|
}`}
|
||||||
></span>
|
></span>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const ComponentCard: React.FC<ComponentCardProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03] ${className}`}
|
className={`rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/3 ${className}`}
|
||||||
>
|
>
|
||||||
{/* Card Header */}
|
{/* Card Header */}
|
||||||
<div className="px-6 py-5 flex items-center justify-between">
|
<div className="px-6 py-5 flex items-center justify-between">
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ export default function MonthlyNewChart() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overflow-hidden rounded-2xl border border-gray-200 bg-white px-5 pt-5 dark:border-gray-800 dark:bg-white/[0.03] sm:px-6 sm:pt-6">
|
<div className="overflow-hidden rounded-2xl border border-gray-200 bg-white px-5 pt-5 dark:border-gray-800 dark:bg-white/3 sm:px-6 sm:pt-6">
|
||||||
<div className="flex flex-col gap-4 mb-4 sm:flex-row sm:items-center sm:justify-between">
|
<div className="flex flex-col gap-4 mb-4 sm:flex-row sm:items-center sm:justify-between">
|
||||||
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||||
Lượng tạo mới hàng tháng
|
Lượng tạo mới hàng tháng
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export default function DemographicCard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03] sm:p-6">
|
<div className="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/3 sm:p-6">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export default function DetailedStatsTable() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03] md:p-6">
|
<div className="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/3 md:p-6">
|
||||||
<h3 className="mb-4 text-lg font-semibold text-gray-800 dark:text-white/90">
|
<h3 className="mb-4 text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||||
Bảng Số Liệu Chi Tiết (Hôm nay vs Hôm qua)
|
Bảng Số Liệu Chi Tiết (Hôm nay vs Hôm qua)
|
||||||
</h3>
|
</h3>
|
||||||
|
|||||||
@@ -47,8 +47,7 @@ export const EcommerceMetrics = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:gap-6">
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:gap-6">
|
||||||
{/* <!-- Metric Item Start --> */}
|
<div className="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/3 md:p-6">
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03] md:p-6">
|
|
||||||
<div className="flex items-center justify-center w-12 h-12 bg-gray-100 rounded-xl dark:bg-gray-800">
|
<div className="flex items-center justify-center w-12 h-12 bg-gray-100 rounded-xl dark:bg-gray-800">
|
||||||
<GroupIcon className="text-gray-800 size-6 dark:text-white/90" />
|
<GroupIcon className="text-gray-800 size-6 dark:text-white/90" />
|
||||||
</div>
|
</div>
|
||||||
@@ -68,10 +67,8 @@ export const EcommerceMetrics = () => {
|
|||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* <!-- Metric Item End --> */}
|
|
||||||
|
|
||||||
{/* <!-- Metric Item Start --> */}
|
<div className="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/3 md:p-6">
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03] md:p-6">
|
|
||||||
<div className="flex items-center justify-center w-12 h-12 bg-gray-100 rounded-xl dark:bg-gray-800">
|
<div className="flex items-center justify-center w-12 h-12 bg-gray-100 rounded-xl dark:bg-gray-800">
|
||||||
<BoxIconLine className="text-gray-800 dark:text-white/90" />
|
<BoxIconLine className="text-gray-800 dark:text-white/90" />
|
||||||
</div>
|
</div>
|
||||||
@@ -91,7 +88,6 @@ export const EcommerceMetrics = () => {
|
|||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* <!-- Metric Item End --> */}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ export default function MonthlySalesChart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overflow-hidden rounded-2xl border border-gray-200 bg-white px-5 pt-5 dark:border-gray-800 dark:bg-white/[0.03] sm:px-6 sm:pt-6">
|
<div className="overflow-hidden rounded-2xl border border-gray-200 bg-white px-5 pt-5 dark:border-gray-800 dark:bg-white/3 sm:px-6 sm:pt-6">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||||
Monthly Sales
|
Monthly Sales
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default function MonthlyTarget() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-2xl border border-gray-200 bg-gray-100 dark:border-gray-800 dark:bg-white/[0.03]">
|
<div className="rounded-2xl border border-gray-200 bg-gray-100 dark:border-gray-800 dark:bg-white/3">
|
||||||
<div className="px-5 pt-5 bg-white shadow-default rounded-2xl pb-11 dark:bg-gray-900 sm:px-6 sm:pt-6">
|
<div className="px-5 pt-5 bg-white shadow-default rounded-2xl pb-11 dark:bg-gray-900 sm:px-6 sm:pt-6">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ const tableData: Product[] = [
|
|||||||
|
|
||||||
export default function RecentOrders() {
|
export default function RecentOrders() {
|
||||||
return (
|
return (
|
||||||
<div className="overflow-hidden rounded-2xl border border-gray-200 bg-white px-4 pb-3 pt-4 dark:border-gray-800 dark:bg-white/[0.03] sm:px-6">
|
<div className="overflow-hidden rounded-2xl border border-gray-200 bg-white px-4 pb-3 pt-4 dark:border-gray-800 dark:bg-white/3 sm:px-6">
|
||||||
<div className="flex flex-col gap-2 mb-4 sm:flex-row sm:items-center sm:justify-between">
|
<div className="flex flex-col gap-2 mb-4 sm:flex-row sm:items-center sm:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export default function StatisticsChart() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white px-5 pb-5 pt-5 dark:border-gray-800 dark:bg-white/[0.03] sm:px-6 sm:pt-6">
|
<div className="rounded-2xl border border-gray-200 bg-white px-5 pb-5 pt-5 dark:border-gray-800 dark:bg-white/3 sm:px-6 sm:pt-6">
|
||||||
<div className="flex flex-col gap-5 mb-6 sm:flex-row sm:justify-between">
|
<div className="flex flex-col gap-5 mb-6 sm:flex-row sm:justify-between">
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ export default function ApplicationTable({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-white/[0.05] dark:bg-white/[0.03]">
|
<div className="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-white/[0.05] dark:bg-white/3">
|
||||||
<div className="max-w-full overflow-x-auto">
|
<div className="max-w-full overflow-x-auto">
|
||||||
<div className="min-w-[1000px]">
|
<div className="min-w-[1000px]">
|
||||||
<Table>
|
<Table>
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export default function BasicTableOne({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-white/[0.05] dark:bg-white/[0.03]">
|
<div className="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-white/[0.05] dark:bg-white/3">
|
||||||
<div className="max-w-full overflow-x-auto">
|
<div className="max-w-full overflow-x-auto">
|
||||||
<div className="min-w-[1100px]">
|
<div className="min-w-[1100px]">
|
||||||
<Table>
|
<Table>
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export default function MediaTable({
|
|||||||
const isAllSelected = data.length > 0 && selectedIds.length === data.length;
|
const isAllSelected = data.length > 0 && selectedIds.length === data.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-white/[0.05] dark:bg-white/[0.03]">
|
<div className="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-white/[0.05] dark:bg-white/3">
|
||||||
<div className="max-w-full overflow-x-auto">
|
<div className="max-w-full overflow-x-auto">
|
||||||
<div className="min-w-[1000px]">
|
<div className="min-w-[1000px]">
|
||||||
<Table>
|
<Table>
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import { API } from "../../api";
|
|
||||||
|
|
||||||
const axiosInstance = axios.create({
|
|
||||||
baseURL: "/",
|
|
||||||
withCredentials: true,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
axiosInstance.interceptors.response.use(
|
|
||||||
(response) => {
|
|
||||||
if (response.data && response.data.status === false) {
|
|
||||||
return handleRefreshToken(response);
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
},
|
|
||||||
async (error) => {
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
async function handleRefreshToken(originalResponse: any) {
|
|
||||||
try {
|
|
||||||
const refreshRes = await axios.get(API.Auth.REFRESH, { withCredentials: true });
|
|
||||||
|
|
||||||
if (refreshRes.data && refreshRes.data.status !== false) {
|
|
||||||
return axiosInstance(originalResponse.config);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Refresh token failed", err);
|
|
||||||
}
|
|
||||||
return originalResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default axiosInstance;
|
|
||||||
+61
-38
@@ -1,65 +1,88 @@
|
|||||||
import axios from "axios"
|
import axios, { AxiosError, InternalAxiosRequestConfig } from "axios";
|
||||||
import { API_URL_ROOT } from "../../api"
|
|
||||||
|
|
||||||
const baseURL = API_URL_ROOT || "https://history-api.kain.id.vn"
|
export const baseURL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3344";
|
||||||
|
|
||||||
const api = axios.create({
|
export const api = axios.create({
|
||||||
baseURL,
|
baseURL,
|
||||||
withCredentials: true
|
withCredentials: true,
|
||||||
})
|
});
|
||||||
|
|
||||||
let isRefreshing = false
|
interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
|
||||||
let queue: any[] = []
|
_retry?: boolean;
|
||||||
|
|
||||||
const processQueue = (error?: any) => {
|
|
||||||
queue.forEach((p) => {
|
|
||||||
if (error) p.reject(error)
|
|
||||||
else p.resolve()
|
|
||||||
})
|
|
||||||
queue = []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface QueueItem {
|
||||||
|
resolve: () => void;
|
||||||
|
reject: (error: unknown) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
let isRefreshing = false;
|
||||||
|
let queue: QueueItem[] = [];
|
||||||
|
|
||||||
|
const processQueue = (error: unknown = null) => {
|
||||||
|
queue.forEach((p) => {
|
||||||
|
if (error) p.reject(error);
|
||||||
|
else p.resolve();
|
||||||
|
});
|
||||||
|
queue = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const skipRefreshUrls = [
|
||||||
|
"/auth/signin",
|
||||||
|
"/auth/signup",
|
||||||
|
"/auth/logout",
|
||||||
|
"/auth/refresh",
|
||||||
|
"/auth/forgot-password",
|
||||||
|
"/auth/token/create",
|
||||||
|
"/auth/token/verify",
|
||||||
|
];
|
||||||
|
|
||||||
api.interceptors.response.use(
|
api.interceptors.response.use(
|
||||||
(res) => res,
|
(res) => res,
|
||||||
async (err) => {
|
async (err: AxiosError) => {
|
||||||
const originalRequest = err.config
|
const originalRequest = err.config as CustomAxiosRequestConfig;
|
||||||
|
|
||||||
if (err.response?.status === 401 && !originalRequest._retry) {
|
const url = originalRequest?.url || "";
|
||||||
|
|
||||||
|
const shouldSkip = skipRefreshUrls?.some((path) =>
|
||||||
|
url?.includes(path)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
err.response?.status === 401 &&
|
||||||
|
originalRequest &&
|
||||||
|
!originalRequest._retry &&
|
||||||
|
!shouldSkip
|
||||||
|
) {
|
||||||
if (isRefreshing) {
|
if (isRefreshing) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
queue.push({
|
queue.push({
|
||||||
resolve: () => resolve(api(originalRequest)),
|
resolve: () => resolve(api(originalRequest)),
|
||||||
reject
|
reject: (queueErr) => reject(queueErr),
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
originalRequest._retry = true
|
originalRequest._retry = true;
|
||||||
isRefreshing = true
|
isRefreshing = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await axios.post(
|
await axios.post(`${baseURL}/auth/refresh`, {}, { withCredentials: true });
|
||||||
`${baseURL}/auth/refresh`,
|
|
||||||
{},
|
|
||||||
{ withCredentials: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
processQueue()
|
processQueue(null);
|
||||||
|
|
||||||
return api(originalRequest)
|
return api(originalRequest);
|
||||||
} catch (refreshErr) {
|
} catch (refreshErr) {
|
||||||
processQueue(refreshErr)
|
processQueue(refreshErr);
|
||||||
|
|
||||||
window.location.href = "/signin"
|
window.location.href = "/auth/signin"
|
||||||
|
|
||||||
return Promise.reject(refreshErr)
|
return Promise.reject(refreshErr);
|
||||||
} finally {
|
} finally {
|
||||||
isRefreshing = false
|
isRefreshing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(err)
|
return Promise.reject(err);
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
|
|
||||||
export default api
|
|
||||||
|
|||||||
+11
-4
@@ -1,8 +1,15 @@
|
|||||||
export interface CommonResponse<T = any> {
|
export interface ErrorResponse {
|
||||||
|
failed_field: string;
|
||||||
|
tag: string;
|
||||||
|
value: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommonResponse<T = unknown> {
|
||||||
status: boolean;
|
status: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
data: T;
|
data: T;
|
||||||
errors?: any; // Or a more specific error type
|
errors?: ErrorResponse[]; // Or a more specific error type
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PaginatedResponse<T> {
|
export interface PaginatedResponse<T> {
|
||||||
@@ -15,7 +22,7 @@ export interface PaginatedResponse<T> {
|
|||||||
total_records: number;
|
total_records: number;
|
||||||
total_pages: number;
|
total_pages: number;
|
||||||
};
|
};
|
||||||
errors?: any;
|
errors?: ErrorResponse[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CursorPaginatedResponse<T> {
|
export interface CursorPaginatedResponse<T> {
|
||||||
@@ -25,5 +32,5 @@ export interface CursorPaginatedResponse<T> {
|
|||||||
items: T[];
|
items: T[];
|
||||||
next_cursor_id?: string;
|
next_cursor_id?: string;
|
||||||
};
|
};
|
||||||
errors?: any;
|
errors?: ErrorResponse[];
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ export interface ApplicationDto {
|
|||||||
reviewed_at: string | null;
|
reviewed_at: string | null;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
media: any[];
|
media: MediaDto[];
|
||||||
user: {
|
user: {
|
||||||
display_name?: string;
|
display_name?: string;
|
||||||
avatar_url?: string;
|
avatar_url?: string;
|
||||||
|
|||||||
+41
-19
@@ -1,22 +1,46 @@
|
|||||||
export interface Project {
|
export interface CommitSimpleResponse {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
edit_summary: string;
|
||||||
description: string;
|
}
|
||||||
project_status: "PRIVATE" | "PUBLIC" | "ARCHIVE";
|
|
||||||
created_at: string;
|
export interface MemberSimpleResponse {
|
||||||
updated_at: string;
|
user_id: string;
|
||||||
is_deleted?: boolean;
|
role: string;
|
||||||
user_id?: string;
|
display_name: string;
|
||||||
user?: {
|
avatar_url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SubmissionSimpleResponse {
|
||||||
|
id: string;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserSimpleResponse {
|
||||||
id: string;
|
id: string;
|
||||||
email: string;
|
email: string;
|
||||||
display_name: string;
|
display_name: string;
|
||||||
avatar_url: string;
|
avatar_url: string;
|
||||||
};
|
|
||||||
commits?: any[];
|
|
||||||
submission_ids?: any[];
|
|
||||||
members?: ProjectMember[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Project {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
latest_commit_id?: string | null;
|
||||||
|
project_status: "PRIVATE" | "PUBLIC" | "ARCHIVE" | string;
|
||||||
|
locked_by?: string | null;
|
||||||
|
is_deleted: boolean;
|
||||||
|
user_id: string;
|
||||||
|
created_at?: string | null;
|
||||||
|
updated_at?: string | null;
|
||||||
|
|
||||||
|
user?: UserSimpleResponse | null;
|
||||||
|
|
||||||
|
commits: CommitSimpleResponse[];
|
||||||
|
submissions: SubmissionSimpleResponse[];
|
||||||
|
members: MemberSimpleResponse[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface ProjectsResponse<T = Project> {
|
export interface ProjectsResponse<T = Project> {
|
||||||
status: boolean;
|
status: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
@@ -28,24 +52,22 @@ export interface ProjectsResponse<T = Project> {
|
|||||||
total_pages: number;
|
total_pages: number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateProjectPayload {
|
export interface UpdateProjectPayload {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
status: "PRIVATE" | "PUBLIC" | "ARCHIVE";
|
status: "PRIVATE" | "PUBLIC" | "ARCHIVE";
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChangeOwnerPayload {
|
export interface ChangeOwnerPayload {
|
||||||
new_owner_id: string;
|
new_owner_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProjectMemberPayload {
|
export interface ProjectMemberPayload {
|
||||||
user_id?: string;
|
user_id?: string;
|
||||||
role: "EDITOR" | "VIEWER" | "ADMIN";
|
role: "EDITOR" | "VIEWER" | "ADMIN";
|
||||||
}
|
}
|
||||||
export interface ProjectMember {
|
|
||||||
user_id: string;
|
|
||||||
role: string;
|
|
||||||
display_name: string;
|
|
||||||
avatar_url: string;
|
|
||||||
}
|
|
||||||
export interface GetProjectsParams {
|
export interface GetProjectsParams {
|
||||||
page?: number;
|
page?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export default function SidebarWidget() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
mx-auto mb-10 w-full max-w-60 rounded-2xl bg-gray-50 px-4 py-5 text-center dark:bg-white/[0.03]`}
|
mx-auto mb-10 w-full max-w-60 rounded-2xl bg-gray-50 px-4 py-5 text-center dark:bg-white/3`}
|
||||||
>
|
>
|
||||||
<h3 className="mb-2 font-semibold text-gray-900 dark:text-white">
|
<h3 className="mb-2 font-semibold text-gray-900 dark:text-white">
|
||||||
#1 Tailwind CSS Dashboard
|
#1 Tailwind CSS Dashboard
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import api from "@/config/config";
|
import { api } from "@/config/config";
|
||||||
import { API } from "../../api";
|
import { API } from "../../api";
|
||||||
import { getUserDto } from "@/interface/admin";
|
import { getUserDto } from "@/interface/admin";
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import api from "@/config/config";
|
import { api } from "@/config/config";
|
||||||
import { API } from "../../api";
|
import { API } from "../../api";
|
||||||
|
|
||||||
export const apiCreateOTP = async (email: string) => {
|
export const apiCreateOTP = async (email: string) => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import api from "@/config/config";
|
import { api } from "@/config/config";
|
||||||
import { API } from "../../api";
|
import { API } from "../../api";
|
||||||
|
|
||||||
export const createHistorianCV = async (payload: any) => {
|
export const createHistorianCV = async (payload: any) => {
|
||||||
@@ -6,7 +6,7 @@ export const createHistorianCV = async (payload: any) => {
|
|||||||
return response?.data;
|
return response?.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const apiGetUserApplications = async (payload :any) => {
|
export const apiGetUserApplications = async (payload: any) => {
|
||||||
const response = await api.get(API.Historian.APPLICATION, { params: payload });
|
const response = await api.get(API.Historian.APPLICATION, { params: payload });
|
||||||
return response?.data;
|
return response?.data;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import api from "@/config/config";
|
import { api } from "@/config/config";
|
||||||
import { API } from "../../api";
|
import { API } from "../../api";
|
||||||
import { payloadPresignedMedia } from "@/interface/media";
|
import { payloadPresignedMedia } from "@/interface/media";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import api from "@/config/config";
|
import { api } from "@/config/config";
|
||||||
import { API } from "../../api";
|
import { API } from "../../api";
|
||||||
import { ProjectMemberPayload, ChangeOwnerPayload, CreateCommitPayload, GetProjectsParams, Project, RestoreCommitPayload, UpdateProjectPayload } from "@/interface/project";
|
import { ProjectMemberPayload, ChangeOwnerPayload, CreateCommitPayload, GetProjectsParams, Project, RestoreCommitPayload, UpdateProjectPayload } from "@/interface/project";
|
||||||
import { CommonResponse, CursorPaginatedResponse, PaginatedResponse } from "@/interface/common";
|
import { CommonResponse, CursorPaginatedResponse, PaginatedResponse } from "@/interface/common";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import api from "@/config/config";
|
import { api } from "@/config/config";
|
||||||
import { API } from "../../api";
|
import { API } from "../../api";
|
||||||
import { GetStatisticsParams, StatisticResponse } from "@/interface/statistics";
|
import { GetStatisticsParams, StatisticResponse } from "@/interface/statistics";
|
||||||
import { CommonResponse } from "@/interface/common";
|
import { CommonResponse } from "@/interface/common";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import api from "@/config/config";
|
import { api } from "@/config/config";
|
||||||
import { API } from "../../api";
|
import { API } from "../../api";
|
||||||
import { getSubmissionPayload, updateSubmissionPayload } from "@/interface/submission";
|
import { getSubmissionPayload, updateSubmissionPayload } from "@/interface/submission";
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import api from "@/config/config";
|
import { api } from "@/config/config";
|
||||||
import { API } from "../../api";
|
import { API } from "../../api";
|
||||||
import { Profile } from "@/interface/user";
|
import { Profile } from "@/interface/user";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user