From 8bc6b32e265ab7999ba8beb4cb5054473f4c9bfd Mon Sep 17 00:00:00 2001 From: bokhonglo Date: Wed, 22 Apr 2026 16:53:14 +0700 Subject: [PATCH] manage current user's media --- api.ts | 2 + constant.ts | 3 +- .../(forms)/role-upgrade/page.tsx | 2 +- .../applications}/page.tsx | 1 - .../basic-tables/page.tsx | 0 .../user-information}/page.tsx | 0 .../applications/page.tsx | 0 .../{profile => account}/page.tsx | 18 - .../(admin)/(others-pages)/assets/page.tsx | 5 + .../(admin)/(others-pages)/library/page.tsx | 67 +++ src/components/header/UserDropdown.tsx | 39 +- .../user-profile/ApplicationList.tsx | 89 ++-- src/components/user-profile/Media.tsx | 401 ++++++++++++++++-- src/layout/AppHeader.tsx | 4 +- src/layout/AppSidebar.tsx | 68 +-- src/service/mediaService.ts | 8 + 16 files changed, 547 insertions(+), 160 deletions(-) rename src/app/(admin)/(others-pages)/{(tables)/applications-tables => (management)/applications}/page.tsx (99%) rename src/app/(admin)/(others-pages)/{(tables) => (management)}/basic-tables/page.tsx (100%) rename src/app/(admin)/(others-pages)/{(tables)/user-table => (management)/user-information}/page.tsx (100%) rename src/app/(admin)/(others-pages)/{profile => account}/applications/page.tsx (100%) rename src/app/(admin)/(others-pages)/{profile => account}/page.tsx (63%) create mode 100644 src/app/(admin)/(others-pages)/assets/page.tsx create mode 100644 src/app/(admin)/(others-pages)/library/page.tsx diff --git a/api.ts b/api.ts index 9e169e2..4ab55ed 100644 --- a/api.ts +++ b/api.ts @@ -12,6 +12,8 @@ export const API = { Media:{ PRESIGNED: `${API_URL_ROOT}/media/presigned`, GET_MEDIA_BY_ID: (Id: number | string) => `${API_URL_ROOT}/media/${Id}`, + DELETE_MEDIA_BY_ID: (Id: number | string) => `${API_URL_ROOT}/media/${Id}`, + DELETE_MEDIA: `${API_URL_ROOT}/media`, }, Auth : { LOGOUT: `${API_URL_ROOT}/auth/logout`, diff --git a/constant.ts b/constant.ts index d2e48b1..191828f 100644 --- a/constant.ts +++ b/constant.ts @@ -1 +1,2 @@ -export const LIMIT_ITEM_TABLE = 5; \ No newline at end of file +export const LIMIT_ITEM_TABLE = 5; +export const INITIAL_LIMIT = 16; \ No newline at end of file diff --git a/src/app/(admin)/(others-pages)/(forms)/role-upgrade/page.tsx b/src/app/(admin)/(others-pages)/(forms)/role-upgrade/page.tsx index f299f93..17d360e 100644 --- a/src/app/(admin)/(others-pages)/(forms)/role-upgrade/page.tsx +++ b/src/app/(admin)/(others-pages)/(forms)/role-upgrade/page.tsx @@ -198,7 +198,7 @@ export default function RoleUpgrade() { return (
- +
diff --git a/src/app/(admin)/(others-pages)/(tables)/applications-tables/page.tsx b/src/app/(admin)/(others-pages)/(management)/applications/page.tsx similarity index 99% rename from src/app/(admin)/(others-pages)/(tables)/applications-tables/page.tsx rename to src/app/(admin)/(others-pages)/(management)/applications/page.tsx index 38a2d8e..8111d2d 100644 --- a/src/app/(admin)/(others-pages)/(tables)/applications-tables/page.tsx +++ b/src/app/(admin)/(others-pages)/(management)/applications/page.tsx @@ -218,7 +218,6 @@ export default function HistorianApplicationPage() { } > - {/* Cập nhật Grid để chứa đủ các ô filter */}
diff --git a/src/app/(admin)/(others-pages)/(tables)/basic-tables/page.tsx b/src/app/(admin)/(others-pages)/(management)/basic-tables/page.tsx similarity index 100% rename from src/app/(admin)/(others-pages)/(tables)/basic-tables/page.tsx rename to src/app/(admin)/(others-pages)/(management)/basic-tables/page.tsx diff --git a/src/app/(admin)/(others-pages)/(tables)/user-table/page.tsx b/src/app/(admin)/(others-pages)/(management)/user-information/page.tsx similarity index 100% rename from src/app/(admin)/(others-pages)/(tables)/user-table/page.tsx rename to src/app/(admin)/(others-pages)/(management)/user-information/page.tsx diff --git a/src/app/(admin)/(others-pages)/profile/applications/page.tsx b/src/app/(admin)/(others-pages)/account/applications/page.tsx similarity index 100% rename from src/app/(admin)/(others-pages)/profile/applications/page.tsx rename to src/app/(admin)/(others-pages)/account/applications/page.tsx diff --git a/src/app/(admin)/(others-pages)/profile/page.tsx b/src/app/(admin)/(others-pages)/account/page.tsx similarity index 63% rename from src/app/(admin)/(others-pages)/profile/page.tsx rename to src/app/(admin)/(others-pages)/account/page.tsx index 55896f9..8b2c08a 100644 --- a/src/app/(admin)/(others-pages)/profile/page.tsx +++ b/src/app/(admin)/(others-pages)/account/page.tsx @@ -1,22 +1,16 @@ "use client"; import AccountDetails from "@/components/user-profile/AccountDetails"; -import ApplicationList from "@/components/user-profile/ApplicationList"; -import MediaCard from "@/components/user-profile/Media"; import UserInfoCard from "@/components/user-profile/UserInfoCard"; import UserMetaCard from "@/components/user-profile/UserMetaCard"; -import { MediaDto } from "@/interface/media"; import { UserMetaCardProps } from "@/interface/user"; import { apiGetCurrentUser } from "@/service/auth"; -import { apiGetCurrentUserApplications, apiGetCurrentUserMedia } from "@/service/userService"; import { setUserData } from "@/store/features/userSlice"; import { useEffect, useState } from "react"; import { useDispatch } from "react-redux"; export default function Profile() { const [user, setUser] = useState(null); - const [mediaData, setMediaData] = useState(null); - const [applications, setApplications] = useState([]); const [loading, setLoading] = useState(true); const dispatch = useDispatch(); @@ -24,17 +18,7 @@ export default function Profile() { const fetchUser = async () => { try { const userData = await apiGetCurrentUser(); - const mediaResponse = await apiGetCurrentUserMedia(); - const userApplications = await apiGetCurrentUserApplications(); - - console.log("user", userData); - - if (userApplications?.data) { - setApplications(userApplications.data); - } dispatch(setUserData(userData.data)); - - setMediaData(mediaResponse); setUser(userData); } catch (err) { console.error("Lỗi:", err); @@ -54,8 +38,6 @@ export default function Profile() {
- {(mediaData?.data?.length ?? 0) > 0 && } -
diff --git a/src/app/(admin)/(others-pages)/assets/page.tsx b/src/app/(admin)/(others-pages)/assets/page.tsx new file mode 100644 index 0000000..db41437 --- /dev/null +++ b/src/app/(admin)/(others-pages)/assets/page.tsx @@ -0,0 +1,5 @@ +export default function Page() { + return ( +
Page
+ ) +} diff --git a/src/app/(admin)/(others-pages)/library/page.tsx b/src/app/(admin)/(others-pages)/library/page.tsx new file mode 100644 index 0000000..b455f09 --- /dev/null +++ b/src/app/(admin)/(others-pages)/library/page.tsx @@ -0,0 +1,67 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useDispatch } from "react-redux"; +import ApplicationLibrary from "@/components/user-profile/ApplicationList"; +import { MediaDto } from "@/interface/media"; +import { apiGetCurrentUserApplications, apiGetCurrentUserMedia } from "@/service/userService"; +import MediaLibrary from "@/components/user-profile/Media"; + +export default function LibraryPage() { + const [mediaData, setMediaData] = useState(null); + const [applications, setApplications] = useState([]); + const [loading, setLoading] = useState(true); + const dispatch = useDispatch(); + + useEffect(() => { + const fetchLibraryContent = async () => { + try { + const [mediaResponse, userApplications] = await Promise.all([ + apiGetCurrentUserMedia(), + apiGetCurrentUserApplications() + ]); + + if (userApplications?.data) setApplications(userApplications.data); + setMediaData(mediaResponse); + } catch (err) { + console.error("Lỗi khi tải thư viện:", err); + } finally { + setLoading(false); + } + }; + fetchLibraryContent(); + }, []); + + if (loading) { + return ( +
+
+
+ ); + } + + return ( +
+
+

+ Thư viện +

+ +
+ +
+ {(mediaData?.data?.length ?? 0) > 0 && ( +
+ +
+ )} + + {applications.length > 0 && ( +
+ +
+ )} +
+
+ ); +} \ No newline at end of file diff --git a/src/components/header/UserDropdown.tsx b/src/components/header/UserDropdown.tsx index 59832e7..71393e6 100644 --- a/src/components/header/UserDropdown.tsx +++ b/src/components/header/UserDropdown.tsx @@ -8,6 +8,7 @@ import { fullDataUser } from "@/interface/admin"; import { UserMetaCardProps } from "@/interface/user"; import { apiGetCurrentUser, apiLogout } from "@/service/auth"; import { useRouter } from "next/navigation"; +import { ListIcon } from "@/icons"; export default function UserDropdown() { const router = useRouter(); @@ -118,7 +119,7 @@ export default function UserDropdown() { - Edit profile + Tài Khoản - {/*
  • +
  • - - - - Account settings + + + + Nhà Sử Học -
  • */} -
  • +
  • + {/*
  • - Support + Hỗ Trợ -
  • + */} - Sign out + Đăng xuất
    diff --git a/src/components/user-profile/ApplicationList.tsx b/src/components/user-profile/ApplicationList.tsx index e8b51a4..527870d 100644 --- a/src/components/user-profile/ApplicationList.tsx +++ b/src/components/user-profile/ApplicationList.tsx @@ -46,7 +46,7 @@ const processMedia = (mediaArray: any[]) => { return { type: "empty" }; }; -export default function ApplicationSquareCardList({ +export default function ApplicationList({ applications, }: { applications: any[]; @@ -56,9 +56,9 @@ export default function ApplicationSquareCardList({ const handleViewDetail = (app: any) => { dispatch(setSelectedApplication(app)); - router.push(`/profile/applications`); + router.push(`/account/applications`); }; - // Tạo object mapping để render icon dựa trên status + const StatusIcons: Record = { APPROVED: ( -

    - Applications CV -

    +
    +
    +

    + Hồ sơ{" "} + + ({applications.length} tệp) + +

    + {/*
    + Cập nhật lần cuối: {new Date().toLocaleDateString("vi-VN")} +
    */} +
    -
    + {/* CHỈ SỬA DÒNG NÀY: Tăng số lượng cột (grid-cols) để các thẻ nhỏ lại */} +
    {applications?.map((app) => { const mediaState = processMedia(app.media); const config = statusConfig[app.status] || statusConfig.PENDING; @@ -125,77 +134,69 @@ export default function ApplicationSquareCardList({
    handleViewDetail(app)} - className="group relative h-60 aspect-square border dark:border-zinc-800 rounded-xl cursor-pointer overflow-hidden transition-all duration-300 hover:ring-2 hover:ring-blue-500/50" + className="group relative flex aspect-square w-full cursor-pointer flex-col overflow-hidden rounded-xl border border-gray-200 bg-gray-50 transition-all duration-300 hover:ring-2 hover:ring-blue-500/50 dark:border-zinc-700 dark:bg-zinc-800" > - {/* Media Layer */}
    {mediaState.type === "image" ? ( ) : ( -
    +
    {mediaState.type === "documents" ? ( -
    +
    {mediaState.extensions?.slice(0, 3).map((ext, i) => ( .{ext} ))}
    ) : ( -
    +
    )}
    )}
    - {/* Overlay Gradient */} -
    +
    - {/* TOP INFO: FILE COUNT & STATUS ICON */} -
    +
    {app.media?.length > 0 ? ( - - {app.media.length} FILE + + {app.media.length} TỆP ) : (
    )} - - {/* Status Icon Wrapper */} -
    +
    {StatusIcons[app.status] || StatusIcons.PENDING}
    - {/* Bottom Content */} -
    -
    -

    - {app.verify_type || "VERIFY"} -

    - {app?.reviewer?.display_name && ( -

    - By: {app.reviewer.display_name} -

    - )} -
    +
    +

    + {app.verify_type || "VERIFY"} +

    -
    -

    + {app?.reviewer?.display_name && ( +

    + Người duyệt: {app.reviewer.display_name} +

    + )} + +
    +

    {formatFullDateTime(app.created_at)}

    -
    + +
    @@ -216,4 +217,4 @@ export default function ApplicationSquareCardList({
    ); -} +} \ No newline at end of file diff --git a/src/components/user-profile/Media.tsx b/src/components/user-profile/Media.tsx index 98f169d..c592e6a 100644 --- a/src/components/user-profile/Media.tsx +++ b/src/components/user-profile/Media.tsx @@ -1,71 +1,396 @@ "use client"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import Lightbox from "yet-another-react-lightbox"; import Zoom from "yet-another-react-lightbox/plugins/zoom"; import Captions from "yet-another-react-lightbox/plugins/captions"; import "yet-another-react-lightbox/styles.css"; import "yet-another-react-lightbox/plugins/captions.css"; +import Swal from "sweetalert2"; import { MediaDto } from "@/interface/media"; import { URL_MEDIA } from "../../../api"; -export default function MediaCard({ data }: { data: MediaDto }) { - const [index, setIndex] = useState(-1); - const listMedia = data?.data || []; +import { deleteMedia } from "@/service/mediaService"; +import { INITIAL_LIMIT } from "../../../constant"; - const slides = listMedia.map((item) => ({ +export default function MediaLibrary({ + data, + onRefresh, +}: { + data: MediaDto; + onRefresh?: () => void; +}) { + const [index, setIndex] = useState(-1); + + const [showAllImages, setShowAllImages] = useState(false); + const [showAllDocs, setShowAllDocs] = useState(false); + + const [localMedia, setLocalMedia] = useState(data?.data || []); + const [isSelectionMode, setIsSelectionMode] = useState(false); + const [selectedIds, setSelectedIds] = useState([]); + + useEffect(() => { + setLocalMedia(data?.data || []); + }, [data]); + + const isImageFile = (file: any) => { + const isImageMime = file.mime_type?.startsWith("image/"); + const isImageExt = /\.(jpg|jpeg|png|webp|gif)$/i.test(file.storage_key); + return isImageMime || isImageExt; + }; + + const imageFiles = localMedia.filter(isImageFile); + const documentFiles = localMedia.filter((file) => !isImageFile(file)); + + const displayedImages = showAllImages + ? imageFiles + : imageFiles.slice(0, INITIAL_LIMIT); + const displayedDocs = showAllDocs + ? documentFiles + : documentFiles.slice(0, INITIAL_LIMIT); + + const imageSlides = imageFiles.map((item) => ({ src: `${URL_MEDIA}${item.storage_key}`, title: item.original_name, - description: `Size: ${(item.size / 1024).toFixed(2)} KB - Type: ${item.mime_type}`, + description: `Kích thước: ${(item.size / 1024).toFixed(2)} KB - Loại: ${item.mime_type}`, })); - // console.log("slides", listMedia); - return ( -
    -

    Media Assets

    -
    - {listMedia.map((item, idx) => ( -
    setIndex(idx)} - className="group relative min-w-[150px] h-[150px] cursor-pointer overflow-hidden rounded-lg border border-gray-200" - > + const toggleSelectionMode = () => { + setIsSelectionMode(!isSelectionMode); + if (isSelectionMode) { + setSelectedIds([]); + } + }; + + const toggleItemSelection = (id: string, e?: React.MouseEvent) => { + if (e) e.stopPropagation(); + + if (!isSelectionMode) { + setIsSelectionMode(true); + setSelectedIds([id]); + return; + } + + if (selectedIds.includes(id)) { + setSelectedIds(selectedIds.filter((selectedId) => selectedId !== id)); + } else { + setSelectedIds([...selectedIds, id]); + } + }; + + const handleItemClick = (item: any, idx: number, isImage: boolean) => { + if (isSelectionMode) { + toggleItemSelection(item.id); + } else { + if (isImage) { + setIndex(idx); + } else { + const fileUrl = `${URL_MEDIA}${item.storage_key}`; + const googleDocsUrl = `https://docs.google.com/viewer?url=${encodeURIComponent(fileUrl)}&embedded=true`; + window.open(googleDocsUrl, "_blank"); + } + } + }; + + const handleDeleteSelected = async () => { + const result = await Swal.fire({ + title: "Xóa tệp đính kèm?", + text: `Bạn chuẩn bị xóa ${selectedIds.length} tệp. Hành động này không thể hoàn tác!`, + icon: "warning", + showCancelButton: true, + confirmButtonColor: "#ef4444", + cancelButtonColor: "#6b7280", + confirmButtonText: "Xóa vĩnh viễn", + cancelButtonText: "Hủy", + }); + + if (result.isConfirmed) { + try { + await deleteMedia(selectedIds); + setLocalMedia((prev) => + prev.filter((item) => !selectedIds.includes(item.id)), + ); + setIsSelectionMode(false); + setSelectedIds([]); + Swal.fire("Thành công!", "Các tệp đã được xóa.", "success"); + if (onRefresh) onRefresh(); + } catch (error) { + Swal.fire("Lỗi!", "Không thể xóa tệp, vui lòng thử lại.", "error"); + } + } + }; + + const handleDeleteFromLightbox = async () => { + const currentImage = imageFiles[index]; + if (!currentImage) return; + + const result = await Swal.fire({ + title: "Xóa ảnh này?", + text: "Bạn có chắc chắn muốn xóa ảnh này không?", + icon: "warning", + showCancelButton: true, + confirmButtonColor: "#ef4444", + cancelButtonColor: "#6b7280", + confirmButtonText: "Xóa", + cancelButtonText: "Hủy", + // customClass: { container: 'z-99999999999999999999999' } + }); + + if (result.isConfirmed) { + try { + await deleteMedia([currentImage.id]); + setLocalMedia((prev) => + prev.filter((item) => item.id !== currentImage.id), + ); + + if (imageFiles.length === 1) { + setIndex(-1); + } else if (index >= imageFiles.length - 1) { + setIndex(index - 1); + } + + Swal.fire({ + title: "Thành công!", + text: "Ảnh đã được xóa.", + icon: "success", + customClass: { container: "z-[9999999999]" }, + }); + if (onRefresh) onRefresh(); + } catch (error) { + Swal.fire({ + title: "Lỗi!", + text: "Không thể xóa ảnh.", + icon: "error", + customClass: { container: "z-[9999999999]" }, + }); + } + } + }; + + const renderItemCard = (item: any, isImage: boolean, idx: number) => { + const isSelected = selectedIds.includes(item.id); + + return ( +
    handleItemClick(item, idx, isImage)} + className={`group relative aspect-square cursor-pointer overflow-hidden rounded-xl border-2 transition-all ${ + isSelected + ? "border-blue-500 ring-2 ring-blue-500/30" + : "border-gray-200 hover:ring-2 hover:ring-blue-500 hover:ring-offset-2 dark:border-zinc-700 dark:hover:ring-offset-zinc-900" + } ${isImage ? "bg-gray-100 dark:bg-zinc-800" : "bg-gray-50 dark:bg-zinc-800"}`} + > +
    toggleItemSelection(item.id, e)} + className={`absolute right-2 top-2 z-30 flex h-6 w-6 items-center justify-center rounded-full border-2 transition-all duration-200 ${ + isSelected + ? "border-blue-500 bg-blue-500" + : "border-white bg-black/40 opacity-0 group-hover:opacity-100" + } ${isSelectionMode && !isSelected ? "opacity-100" : ""}`} + > + {isSelected && ( + + + + )} +
    + + {isImage ? ( + <> {item.original_name} - -
    -

    - {item.original_name} -

    +
    +
    +

    + {item.original_name} +

    +

    + {(item.size / 1024).toFixed(0)} KB +

    +
    + {!isSelectionMode && ( +
    + + Xem ảnh + +
    + )} + + ) : ( + <> +
    +
    + 📄 +
    + + {item.original_name} + +
    + {!isSelectionMode && ( +
    + + Xem file + +
    + )} + + )} +
    + ); + }; + + return ( +
    +
    +

    + Media Assets{" "} + + ({localMedia.length} tệp) + +

    + +
    + {selectedIds.length > 0 && ( + + )} + + +
    +
    + +
    + {imageFiles.length > 0 && ( +
    +

    + Hình ảnh ({imageFiles.length}) +

    +
    + {" "} + {displayedImages.map((item, idx) => + renderItemCard(item, true, idx), + )} +
    + {imageFiles.length > INITIAL_LIMIT && ( +
    + +
    + )}
    - ))} + )} + + {documentFiles.length > 0 && ( +
    +

    + Tài liệu ({documentFiles.length}) +

    +
    + {" "} + {displayedDocs.map((item, idx) => + renderItemCard(item, false, idx), + )} +
    + {documentFiles.length > INITIAL_LIMIT && ( +
    + +
    + )} +
    + )}
    = 0} close={() => setIndex(-1)} - slides={slides} + slides={imageSlides} plugins={[Zoom, Captions]} - zoom={{ - maxZoomPixelRatio: 10, - zoomInMultiplier: 2, - doubleTapDelay: 300, - doubleClickDelay: 300, - doubleClickMaxStops: 2, - keyboardMoveDistance: 50, - wheelZoomDistanceFactor: 100, - pinchZoomDistanceFactor: 100, + toolbar={{ + buttons: [ + , + "zoom", + "close", + ], }} + zoom={{ maxZoomPixelRatio: 3 }} animation={{ zoom: 200 }} styles={{ - root: { - zIndex: 999999999, - "--yarl__color_backdrop": "rgba(0, 0, 0, 0.85)", - }, + root: { zIndex: 99, "--yarl__color_backdrop": "rgba(0, 0, 0, 0.9)" }, }} />
    diff --git a/src/layout/AppHeader.tsx b/src/layout/AppHeader.tsx index 479ed20..53d95c3 100644 --- a/src/layout/AppHeader.tsx +++ b/src/layout/AppHeader.tsx @@ -162,10 +162,10 @@ const AppHeader: React.FC = () => { >
    {/* */} - + {/* */} {/* */} - + {/* */} {/* */}
    {/* */} diff --git a/src/layout/AppSidebar.tsx b/src/layout/AppSidebar.tsx index 83bdd1f..158bda3 100644 --- a/src/layout/AppSidebar.tsx +++ b/src/layout/AppSidebar.tsx @@ -10,6 +10,7 @@ import { BoxCubeIcon, CalenderIcon, ChevronDownIcon, + FileIcon, GridIcon, HorizontaLDots, ListIcon, @@ -39,44 +40,51 @@ type NavItem = { const ALL_NAV_ITEMS: NavItem[] = [ { icon: , - name: "Dashboard", - subItems: [{ name: "Ecommerce", path: "/", pro: false }], + name: "Trang Chủ", + // subItems: [{ name: "Ecommerce", path: "/", pro: false }], + path: "/", }, + // { + // icon: , + // name: "Calendar", + // path: "/calendar", + // }, { - icon: , - name: "Calendar", - path: "/calendar", + icon: , + name: "Thư Viện", + path: "/library", }, + // { + // name: "Forms", + // icon: , + // subItems: [ + // // { name: "Form Elements", path: "/form-elements", pro: false }, + // { name: "Role Upgrade", path: "/role-upgrade", pro: false } + // ], + // }, { - icon: , - name: "User Profile", - path: "/profile", - }, - { - name: "Forms", - icon: , - subItems: [ - // { name: "Form Elements", path: "/form-elements", pro: false }, - { name: "Role Upgrade", path: "/role-upgrade", pro: false } - ], - }, - { - name: "Tables", + name: "Quản lý", icon: , subItems: [ // { name: "Basic Tables", path: "/basic-tables", pro: false }, - { name: "User Tables", path: "/user-table", pro: false, roles: ["ADMIN", "MOD"] }, - { name: "Applications Tables", path: "/applications-tables", pro: false, roles: ["ADMIN", "MOD"] }, + { name: "Tài Khoản", path: "/user-information", pro: false, roles: ["ADMIN", "MOD"] }, + { name: "Hồ Sơ Nhà Sử Học", path: "/applications", pro: false, roles: ["ADMIN", "MOD"] }, + { name: "Tệp Đăng Tải", path: "/assets", pro: false, roles: ["ADMIN", "MOD"] }, ], }, { - name: "Pages", - icon: , - subItems: [ - { name: "Blank Page", path: "/blank", pro: false }, - { name: "404 Error", path: "/error-404", pro: false }, - ], + icon: , + name: "Tài Khoản", + path: "/account", }, + // { + // name: "Pages", + // icon: , + // subItems: [ + // { name: "Blank Page", path: "/blank", pro: false }, + // { name: "404 Error", path: "/error-404", pro: false }, + // ], + // }, ]; const OTHERS_ITEMS: NavItem[] = [ @@ -198,7 +206,7 @@ const AppSidebar: React.FC = () => { {nav.subItems ? (
    -
    + {/*

    {isExpanded || isHovered || isMobileOpen ? "Others" : }

    {renderMenuItems(filteredOthersItems, "others")} -
    +
    */}
    diff --git a/src/service/mediaService.ts b/src/service/mediaService.ts index e9229d4..3ff98d9 100644 --- a/src/service/mediaService.ts +++ b/src/service/mediaService.ts @@ -109,3 +109,11 @@ export const getMediaById = async (mediaId: number | string) => { return response?.data; } +export const deleteMedia = async (mediaIds: string[]) => { + const response = await api.delete(API.Media.DELETE_MEDIA, { + data: { + media_ids: mediaIds + } + }); + return response?.data; +} \ No newline at end of file