feat: implement core admin dashboard infrastructure including service layers, UI components, and submission management pages

This commit is contained in:
2026-05-13 17:02:15 +07:00
parent c13ddb37fc
commit f796ff9a96
32 changed files with 171 additions and 162 deletions
@@ -13,7 +13,7 @@ export default function Profile() {
const [user, setUser] = useState<UserMetaCardProps | null>(null); const [user, setUser] = useState<UserMetaCardProps | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const dispatch = useDispatch(); const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
const fetchUser = async () => { const fetchUser = async () => {
try { try {
@@ -28,10 +28,10 @@ export default function Profile() {
}; };
fetchUser(); fetchUser();
}, []); }, []);
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);
} }
+6 -6
View File
@@ -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
View File
@@ -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;
+9 -10
View File
@@ -87,12 +87,12 @@ const Calendar: React.FC = () => {
prevEvents.map((event) => prevEvents.map((event) =>
event.id === selectedEvent.id event.id === selectedEvent.id
? { ? {
...event, ...event,
title: eventTitle, title: eventTitle,
start: eventStartDate, start: eventStartDate,
end: eventEndDate, end: eventEndDate,
extendedProps: { calendar: eventLevel }, extendedProps: { calendar: eventLevel },
} }
: event : event
) )
); );
@@ -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,9 +201,8 @@ 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>
</span> </span>
+2 -2
View File
@@ -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">
@@ -31,7 +31,7 @@ const ComponentCard: React.FC<ComponentCardProps> = ({
)} )}
{headerAction && <div>{headerAction}</div>} {headerAction && <div>{headerAction}</div>}
</div> </div>
{/* Card Body */} {/* Card Body */}
<div className="p-4 border-t border-gray-100 dark:border-gray-800 sm:p-6"> <div className="p-4 border-t border-gray-100 dark:border-gray-800 sm:p-6">
<div className="space-y-6">{children}</div> <div className="space-y-6">{children}</div>
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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>
+3 -3
View File
@@ -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">
@@ -194,8 +194,8 @@ export default function RecentOrders() {
product.status === "Delivered" product.status === "Delivered"
? "success" ? "success"
: product.status === "Pending" : product.status === "Pending"
? "warning" ? "warning"
: "error" : "error"
} }
> >
{product.status} {product.status}
+1 -1
View File
@@ -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">
+1 -1
View File
@@ -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>
+5 -5
View File
@@ -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>
@@ -236,10 +236,10 @@ export default function BasicTableOne({
{role.name} {role.name}
</span> </span>
)) || ( )) || (
<span className="text-gray-400 italic text-[10px]"> <span className="text-gray-400 italic text-[10px]">
No Role No Role
</span> </span>
)} )}
</div> </div>
</TableCell> </TableCell>
+1 -1
View File
@@ -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>
-37
View File
@@ -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
View File
@@ -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
View File
@@ -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[];
} }
+1 -1
View File
@@ -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;
+42 -20
View File
@@ -1,22 +1,46 @@
export interface CommitSimpleResponse {
id: string;
edit_summary: string;
}
export interface MemberSimpleResponse {
user_id: string;
role: string;
display_name: string;
avatar_url: string;
}
export interface SubmissionSimpleResponse {
id: string;
status: string;
}
export interface UserSimpleResponse {
id: string;
email: string;
display_name: string;
avatar_url: string;
}
export interface Project { export interface Project {
id: string; id: string;
title: string; title: string;
description: string; description: string;
project_status: "PRIVATE" | "PUBLIC" | "ARCHIVE"; latest_commit_id?: string | null;
created_at: string; project_status: "PRIVATE" | "PUBLIC" | "ARCHIVE" | string;
updated_at: string; locked_by?: string | null;
is_deleted?: boolean; is_deleted: boolean;
user_id?: string; user_id: string;
user?: { created_at?: string | null;
id: string; updated_at?: string | null;
email: string;
display_name: string; user?: UserSimpleResponse | null;
avatar_url: string;
}; commits: CommitSimpleResponse[];
commits?: any[]; submissions: SubmissionSimpleResponse[];
submission_ids?: any[]; members: MemberSimpleResponse[];
members?: ProjectMember[];
} }
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;
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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";
@@ -38,6 +38,6 @@ export const apiUpdateApplicationStatus = async (id: string, payload: any) => {
}; };
export const apiGetUserById = async (userId: string) => { export const apiGetUserById = async (userId: string) => {
const response = await api.get(API.Admin.GET_USER_BY_ID(userId)); const response = await api.get(API.Admin.GET_USER_BY_ID(userId));
return response?.data; return response?.data;
}; };
+5 -5
View File
@@ -1,11 +1,11 @@
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) => {
const token_type = 2; const token_type = 2;
const response = await api.post(API.Auth.CREATEOTP, { const response = await api.post(API.Auth.CREATEOTP, {
email, email,
token_type token_type
}); });
return response.data; return response.data;
}; };
@@ -13,7 +13,7 @@ export const apiCreateOTP = async (email: string) => {
export const apiVerifyOTP = async (email: string, token: string) => { export const apiVerifyOTP = async (email: string, token: string) => {
const body = { email, token, token_type: 2 }; const body = { email, token, token_type: 2 };
const response = await api.post(API.Auth.VERIFYOTP, body); const response = await api.post(API.Auth.VERIFYOTP, body);
return response.data; return response.data;
}; };
export const apiSignUp = async (payload: any) => { export const apiSignUp = async (payload: any) => {
+2 -2
View File
@@ -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;
}; };
+2 -2
View File
@@ -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";
@@ -112,7 +112,7 @@ export const getMediaById = async (mediaId: number | string) => {
export const deleteMedia = async (mediaIds: string[]) => { export const deleteMedia = async (mediaIds: string[]) => {
const response = await api.delete(API.Media.DELETE_MEDIA, { const response = await api.delete(API.Media.DELETE_MEDIA, {
data: { data: {
media_ids: mediaIds media_ids: mediaIds
} }
}); });
return response?.data; return response?.data;
+1 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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";