mirror of
https://github.com/usememos/memos.git
synced 2024-12-19 09:02:49 +03:00
refactor: update resources page
This commit is contained in:
parent
d1156aa755
commit
723e6bcdae
@ -1,6 +1,5 @@
|
|||||||
import { getDateTimeString } from "@/helpers/datetime";
|
import { getDateTimeString } from "@/helpers/datetime";
|
||||||
import ResourceIcon from "./ResourceIcon";
|
import ResourceIcon from "./ResourceIcon";
|
||||||
import ResourceItemDropdown from "./ResourceItemDropdown";
|
|
||||||
import "@/less/resource-card.less";
|
import "@/less/resource-card.less";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -10,13 +9,8 @@ interface Props {
|
|||||||
const ResourceCard = ({ resource }: Props) => {
|
const ResourceCard = ({ resource }: Props) => {
|
||||||
return (
|
return (
|
||||||
<div className="resource-card">
|
<div className="resource-card">
|
||||||
<div className="w-full p-2 flex flex-row justify-end items-center absolute top-0 left-0">
|
|
||||||
<div className="more-action-btn">
|
|
||||||
<ResourceItemDropdown resource={resource} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="w-full flex flex-row justify-center items-center pb-2 pt-4 px-2">
|
<div className="w-full flex flex-row justify-center items-center pb-2 pt-4 px-2">
|
||||||
<ResourceIcon resource={resource} strokeWidth={1} />
|
<ResourceIcon resource={resource} strokeWidth={0.5} />
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-col justify-start items-center px-1 select-none">
|
<div className="w-full flex flex-col justify-start items-center px-1 select-none">
|
||||||
<div className="w-full text-base text-center text-ellipsis overflow-hidden line-clamp-3">{resource.filename}</div>
|
<div className="w-full text-base text-center text-ellipsis overflow-hidden line-clamp-3">{resource.filename}</div>
|
||||||
|
@ -1,93 +0,0 @@
|
|||||||
import copy from "copy-to-clipboard";
|
|
||||||
import React from "react";
|
|
||||||
import toast from "react-hot-toast";
|
|
||||||
import { useResourceStore } from "@/store/module";
|
|
||||||
import { useTranslate } from "@/utils/i18n";
|
|
||||||
import { getResourceType, getResourceUrl } from "@/utils/resource";
|
|
||||||
import showChangeResourceFilenameDialog from "./ChangeResourceFilenameDialog";
|
|
||||||
import { showCommonDialog } from "./Dialog/CommonDialog";
|
|
||||||
import Icon from "./Icon";
|
|
||||||
import showPreviewImageDialog from "./PreviewImageDialog";
|
|
||||||
import Dropdown from "./kit/Dropdown";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
resource: Resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ResourceItemDropdown = ({ resource }: Props) => {
|
|
||||||
const t = useTranslate();
|
|
||||||
const resourceStore = useResourceStore();
|
|
||||||
|
|
||||||
const handlePreviewBtnClick = (resource: Resource) => {
|
|
||||||
const resourceUrl = getResourceUrl(resource);
|
|
||||||
if (getResourceType(resource).startsWith("image")) {
|
|
||||||
showPreviewImageDialog([getResourceUrl(resource)], 0);
|
|
||||||
} else {
|
|
||||||
window.open(resourceUrl);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCopyResourceLinkBtnClick = (resource: Resource) => {
|
|
||||||
const url = getResourceUrl(resource);
|
|
||||||
copy(url);
|
|
||||||
toast.success(t("message.succeed-copy-resource-link"));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRenameBtnClick = (resource: Resource) => {
|
|
||||||
showChangeResourceFilenameDialog(resource.id, resource.filename);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteResourceBtnClick = (resource: Resource) => {
|
|
||||||
let warningText = t("resource.warning-text");
|
|
||||||
if (resource.linkedMemoAmount > 0) {
|
|
||||||
warningText = warningText + `\n${t("resource.linked-amount")}: ${resource.linkedMemoAmount}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
showCommonDialog({
|
|
||||||
title: t("resource.delete-resource"),
|
|
||||||
content: warningText,
|
|
||||||
style: "warning",
|
|
||||||
dialogName: "delete-resource-dialog",
|
|
||||||
onConfirm: async () => {
|
|
||||||
await resourceStore.deleteResourceById(resource.id);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dropdown
|
|
||||||
actionsClassName="!w-auto min-w-[8rem]"
|
|
||||||
trigger={<Icon.MoreVertical className="w-4 h-auto hover:opacity-80 cursor-pointer" />}
|
|
||||||
actions={
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
className="w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
|
|
||||||
onClick={() => handlePreviewBtnClick(resource)}
|
|
||||||
>
|
|
||||||
{t("common.preview")}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
|
|
||||||
onClick={() => handleCopyResourceLinkBtnClick(resource)}
|
|
||||||
>
|
|
||||||
{t("resource.copy-link")}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
|
|
||||||
onClick={() => handleRenameBtnClick(resource)}
|
|
||||||
>
|
|
||||||
{t("common.rename")}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded text-red-600 hover:bg-gray-100 dark:hover:bg-zinc-600"
|
|
||||||
onClick={() => handleDeleteResourceBtnClick(resource)}
|
|
||||||
>
|
|
||||||
{t("common.delete")}
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default React.memo(ResourceItemDropdown);
|
|
@ -1,44 +0,0 @@
|
|||||||
import { useRef, useState } from "react";
|
|
||||||
import useDebounce from "react-use/lib/useDebounce";
|
|
||||||
import { useTranslate } from "@/utils/i18n";
|
|
||||||
import Icon from "./Icon";
|
|
||||||
|
|
||||||
interface ResourceSearchBarProps {
|
|
||||||
setQuery: (queryText: string) => void;
|
|
||||||
}
|
|
||||||
const ResourceSearchBar = ({ setQuery }: ResourceSearchBarProps) => {
|
|
||||||
const t = useTranslate();
|
|
||||||
const [queryText, setQueryText] = useState("");
|
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
|
||||||
|
|
||||||
const handleTextQueryInput = (event: React.FormEvent<HTMLInputElement>) => {
|
|
||||||
const text = event.currentTarget.value;
|
|
||||||
setQueryText(text);
|
|
||||||
};
|
|
||||||
|
|
||||||
useDebounce(
|
|
||||||
() => {
|
|
||||||
setQuery(queryText);
|
|
||||||
},
|
|
||||||
200,
|
|
||||||
[queryText]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-44 sm:w-52">
|
|
||||||
<div className="w-full h-9 flex flex-row justify-start items-center py-2 px-3 rounded-md bg-gray-200 dark:bg-zinc-800">
|
|
||||||
<Icon.Search className="w-4 h-auto opacity-30 dark:text-gray-200" />
|
|
||||||
<input
|
|
||||||
className="flex ml-2 w-24 grow text-sm outline-none bg-transparent dark:text-gray-200"
|
|
||||||
type="text"
|
|
||||||
placeholder={t("resource.search-bar-placeholder")}
|
|
||||||
ref={inputRef}
|
|
||||||
value={queryText}
|
|
||||||
onChange={handleTextQueryInput}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ResourceSearchBar;
|
|
67
web/src/pages/Resources.tsx
Normal file
67
web/src/pages/Resources.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import { toast } from "react-hot-toast";
|
||||||
|
import Empty from "@/components/Empty";
|
||||||
|
import Icon from "@/components/Icon";
|
||||||
|
import MobileHeader from "@/components/MobileHeader";
|
||||||
|
import ResourceCard from "@/components/ResourceCard";
|
||||||
|
import useLoading from "@/hooks/useLoading";
|
||||||
|
import { useResourceStore } from "@/store/module";
|
||||||
|
import { useTranslate } from "@/utils/i18n";
|
||||||
|
|
||||||
|
const Resources = () => {
|
||||||
|
const t = useTranslate();
|
||||||
|
const loadingState = useLoading();
|
||||||
|
const resourceStore = useResourceStore();
|
||||||
|
const resources = resourceStore.state.resources;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
resourceStore
|
||||||
|
.fetchResourceList()
|
||||||
|
.then(() => {
|
||||||
|
loadingState.setFinish();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
toast.error(error.response.data.message);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="w-full max-w-3xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8 bg-zinc-100 dark:bg-zinc-800">
|
||||||
|
<MobileHeader showSearch={false} />
|
||||||
|
<div className="w-full flex flex-col justify-start items-start px-4 py-3 rounded-xl bg-white dark:bg-zinc-700 text-black dark:text-gray-300">
|
||||||
|
<div className="relative w-full flex flex-row justify-between items-center">
|
||||||
|
<p className="px-2 py-1 flex flex-row justify-start items-center cursor-pointer select-none rounded opacity-80 hover:bg-gray-100 dark:hover:bg-zinc-700">
|
||||||
|
<Icon.Paperclip className="w-5 h-auto mr-1" /> {t("common.resources")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-full flex flex-col justify-start items-start mt-4 mb-6">
|
||||||
|
{loadingState.isLoading ? (
|
||||||
|
<div className="w-full h-32 flex flex-col justify-center items-center">
|
||||||
|
<p className="w-full text-center text-base my-6 mt-8">{t("resource.fetching-data")}</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
resources.length === 0
|
||||||
|
? "flex flex-col justify-start items-start w-full"
|
||||||
|
: "w-full h-auto grid grid-cols-2 md:grid-cols-4 gap-6"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{resources.length === 0 ? (
|
||||||
|
<div className="w-full mt-8 mb-8 flex flex-col justify-center items-center italic">
|
||||||
|
<Empty />
|
||||||
|
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
resources.map((resource) => <ResourceCard key={resource.id} resource={resource}></ResourceCard>)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Resources;
|
@ -1,242 +0,0 @@
|
|||||||
import { Button } from "@mui/joy";
|
|
||||||
import { useEffect, useMemo, useState } from "react";
|
|
||||||
import { toast } from "react-hot-toast";
|
|
||||||
import showCreateResourceDialog from "@/components/CreateResourceDialog";
|
|
||||||
import { showCommonDialog } from "@/components/Dialog/CommonDialog";
|
|
||||||
import Empty from "@/components/Empty";
|
|
||||||
import Icon from "@/components/Icon";
|
|
||||||
import MobileHeader from "@/components/MobileHeader";
|
|
||||||
import ResourceCard from "@/components/ResourceCard";
|
|
||||||
import ResourceSearchBar from "@/components/ResourceSearchBar";
|
|
||||||
import Dropdown from "@/components/kit/Dropdown";
|
|
||||||
import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts";
|
|
||||||
import useLoading from "@/hooks/useLoading";
|
|
||||||
import { useResourceStore } from "@/store/module";
|
|
||||||
import { useTranslate } from "@/utils/i18n";
|
|
||||||
|
|
||||||
const ResourcesDashboard = () => {
|
|
||||||
const t = useTranslate();
|
|
||||||
const loadingState = useLoading();
|
|
||||||
const resourceStore = useResourceStore();
|
|
||||||
const resources = resourceStore.state.resources;
|
|
||||||
const [queryText, setQueryText] = useState<string>("");
|
|
||||||
const [dragActive, setDragActive] = useState(false);
|
|
||||||
const [isComplete, setIsComplete] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
resourceStore
|
|
||||||
.fetchResourceListWithLimit(DEFAULT_MEMO_LIMIT)
|
|
||||||
.then((fetchedResource) => {
|
|
||||||
if (fetchedResource.length < DEFAULT_MEMO_LIMIT) {
|
|
||||||
setIsComplete(true);
|
|
||||||
}
|
|
||||||
loadingState.setFinish();
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error(error);
|
|
||||||
toast.error(error.response.data.message);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleDeleteUnusedResourcesBtnClick = async () => {
|
|
||||||
let warningText = t("resource.warning-text-unused");
|
|
||||||
const allResources = await fetchAllResources();
|
|
||||||
const unusedResources = allResources.filter((resource) => {
|
|
||||||
if (resource.linkedMemoAmount === 0) {
|
|
||||||
warningText = warningText + `\n- ${resource.filename}`;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
if (unusedResources.length === 0) {
|
|
||||||
toast.success(t("resource.no-unused-resources"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
showCommonDialog({
|
|
||||||
title: t("resource.delete-resource"),
|
|
||||||
content: warningText,
|
|
||||||
style: "warning",
|
|
||||||
dialogName: "delete-unused-resources",
|
|
||||||
onConfirm: async () => {
|
|
||||||
for (const resource of unusedResources) {
|
|
||||||
await resourceStore.deleteResourceById(resource.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFetchMoreResourceBtnClick = async () => {
|
|
||||||
try {
|
|
||||||
const fetchedResource = await resourceStore.fetchResourceListWithLimit(DEFAULT_MEMO_LIMIT, resources.length);
|
|
||||||
if (fetchedResource.length < DEFAULT_MEMO_LIMIT) {
|
|
||||||
setIsComplete(true);
|
|
||||||
} else {
|
|
||||||
setIsComplete(false);
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error(error);
|
|
||||||
toast.error(error.response.data.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchAllResources = async () => {
|
|
||||||
if (isComplete) {
|
|
||||||
return resources;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadingState.setLoading();
|
|
||||||
try {
|
|
||||||
const allResources = await resourceStore.fetchResourceList();
|
|
||||||
loadingState.setFinish();
|
|
||||||
setIsComplete(true);
|
|
||||||
return allResources;
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error(error);
|
|
||||||
toast.error(error.response.data.message);
|
|
||||||
return resources;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSearchResourceInputChange = async (query: string) => {
|
|
||||||
// to prevent first tiger when page is loaded
|
|
||||||
if (query === queryText) return;
|
|
||||||
await fetchAllResources();
|
|
||||||
setQueryText(query);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDrag = (e: React.DragEvent<HTMLDivElement>) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
if (e.type === "dragenter" || e.type === "dragover") {
|
|
||||||
setDragActive(true);
|
|
||||||
} else if (e.type === "dragleave") {
|
|
||||||
setDragActive(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const resourceList = useMemo(
|
|
||||||
() =>
|
|
||||||
resources
|
|
||||||
.filter((res: Resource) => (queryText === "" ? true : res.filename.toLowerCase().includes(queryText.toLowerCase())))
|
|
||||||
.map((resource) => <ResourceCard key={resource.id} resource={resource}></ResourceCard>),
|
|
||||||
[resources, queryText]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleDrop = async (e: React.DragEvent<HTMLDivElement>) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
setDragActive(false);
|
|
||||||
if (e.dataTransfer.files && e.dataTransfer.files[0]) {
|
|
||||||
await resourceStore.createResourcesWithBlob(e.dataTransfer.files).then(
|
|
||||||
(res) => {
|
|
||||||
for (const resource of res) {
|
|
||||||
toast.success(`${resource.filename} ${t("resource.upload-successfully")}`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(reason) => {
|
|
||||||
toast.error(reason);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section className="w-full max-w-3xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8 bg-zinc-100 dark:bg-zinc-800">
|
|
||||||
<MobileHeader showSearch={false} />
|
|
||||||
<div className="w-full relative" onDragEnter={handleDrag}>
|
|
||||||
{dragActive && (
|
|
||||||
<div
|
|
||||||
className="absolute h-full w-full rounded-xl bg-zinc-800 dark:bg-white opacity-60 z-10"
|
|
||||||
onDragEnter={handleDrag}
|
|
||||||
onDragLeave={handleDrag}
|
|
||||||
onDragOver={handleDrag}
|
|
||||||
onDrop={handleDrop}
|
|
||||||
>
|
|
||||||
<div className="flex h-full w-full">
|
|
||||||
<p className="m-auto text-2xl text-white dark:text-black">{t("resource.file-drag-drop-prompt")}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="w-full flex flex-col justify-start items-start px-4 py-3 rounded-xl bg-white dark:bg-zinc-700 text-black dark:text-gray-300">
|
|
||||||
<div className="relative w-full flex flex-row justify-between items-center">
|
|
||||||
<p className="flex flex-row justify-start items-center select-none rounded">
|
|
||||||
<Icon.Paperclip className="w-5 h-auto mr-1 ml-2" /> {t("common.resources")}
|
|
||||||
</p>
|
|
||||||
<ResourceSearchBar setQuery={handleSearchResourceInputChange} />
|
|
||||||
</div>
|
|
||||||
<div className="w-full flex flex-row justify-end items-center space-x-2 mt-3 z-1">
|
|
||||||
<Button
|
|
||||||
onClick={() =>
|
|
||||||
showCreateResourceDialog({
|
|
||||||
onConfirm: () => {
|
|
||||||
resourceStore.fetchResourceList();
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Icon.Plus className="w-4 h-auto" />
|
|
||||||
</Button>
|
|
||||||
<Dropdown
|
|
||||||
className="drop-shadow-none"
|
|
||||||
actionsClassName="!w-28 rounded-lg drop-shadow-md dark:bg-zinc-800"
|
|
||||||
positionClassName="mt-2 top-full right-0"
|
|
||||||
trigger={
|
|
||||||
<Button variant="outlined">
|
|
||||||
<Icon.MoreVertical className="w-4 h-auto" />
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
actions={
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
className="w-full flex flex-row justify-start items-center content-center text-sm whitespace-nowrap leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
|
|
||||||
onClick={handleDeleteUnusedResourcesBtnClick}
|
|
||||||
>
|
|
||||||
<Icon.Trash2 className="w-4 h-auto mr-2" />
|
|
||||||
{t("resource.clear")}
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="w-full flex flex-col justify-start items-start mt-4 mb-6">
|
|
||||||
{loadingState.isLoading ? (
|
|
||||||
<div className="w-full h-32 flex flex-col justify-center items-center">
|
|
||||||
<p className="w-full text-center text-base my-6 mt-8">{t("resource.fetching-data")}</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
className={
|
|
||||||
resourceList.length === 0
|
|
||||||
? "flex flex-col justify-start items-start w-full"
|
|
||||||
: "w-full h-auto grid grid-cols-2 md:grid-cols-4 md:px-6 gap-6"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{resourceList.length === 0 ? (
|
|
||||||
<div className="w-full mt-8 mb-8 flex flex-col justify-center items-center italic">
|
|
||||||
<Empty />
|
|
||||||
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
resourceList
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col justify-start items-center w-full">
|
|
||||||
<p className="text-sm text-gray-400 italic">
|
|
||||||
{!isComplete && (
|
|
||||||
<span className="cursor-pointer my-6 hover:text-green-600" onClick={handleFetchMoreResourceBtnClick}>
|
|
||||||
{t("memo.fetch-more")}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ResourcesDashboard;
|
|
@ -3,7 +3,7 @@ import { createBrowserRouter, redirect } from "react-router-dom";
|
|||||||
import App from "@/App";
|
import App from "@/App";
|
||||||
import Archived from "@/pages/Archived";
|
import Archived from "@/pages/Archived";
|
||||||
import DailyReview from "@/pages/DailyReview";
|
import DailyReview from "@/pages/DailyReview";
|
||||||
import ResourcesDashboard from "@/pages/ResourcesDashboard";
|
import Resources from "@/pages/Resources";
|
||||||
import Setting from "@/pages/Setting";
|
import Setting from "@/pages/Setting";
|
||||||
import { initialGlobalState, initialUserState } from "@/store/module";
|
import { initialGlobalState, initialUserState } from "@/store/module";
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ const router = createBrowserRouter([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "resources",
|
path: "resources",
|
||||||
element: <ResourcesDashboard />,
|
element: <Resources />,
|
||||||
loader: userStateLoader,
|
loader: userStateLoader,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import * as api from "@/helpers/api";
|
import * as api from "@/helpers/api";
|
||||||
import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts";
|
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
import store, { useAppSelector } from "../";
|
import store, { useAppSelector } from "../";
|
||||||
import { deleteResource, patchResource, setResources, upsertResources } from "../reducer/resource";
|
import { deleteResource, patchResource, setResources } from "../reducer/resource";
|
||||||
import { useGlobalStore } from "./global";
|
import { useGlobalStore } from "./global";
|
||||||
|
|
||||||
const convertResponseModelResource = (resource: Resource): Resource => {
|
const convertResponseModelResource = (resource: Resource): Resource => {
|
||||||
@ -30,16 +29,6 @@ export const useResourceStore = () => {
|
|||||||
store.dispatch(setResources(resourceList));
|
store.dispatch(setResources(resourceList));
|
||||||
return resourceList;
|
return resourceList;
|
||||||
},
|
},
|
||||||
async fetchResourceListWithLimit(limit = DEFAULT_MEMO_LIMIT, offset?: number): Promise<Resource[]> {
|
|
||||||
const resourceFind: ResourceFind = {
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
};
|
|
||||||
const { data } = await api.getResourceListWithLimit(resourceFind);
|
|
||||||
const resourceList = data.map((m) => convertResponseModelResource(m));
|
|
||||||
store.dispatch(upsertResources(resourceList));
|
|
||||||
return resourceList;
|
|
||||||
},
|
|
||||||
async createResource(resourceCreate: ResourceCreate): Promise<Resource> {
|
async createResource(resourceCreate: ResourceCreate): Promise<Resource> {
|
||||||
const { data } = await api.createResource(resourceCreate);
|
const { data } = await api.createResource(resourceCreate);
|
||||||
const resource = convertResponseModelResource(data);
|
const resource = convertResponseModelResource(data);
|
||||||
|
Loading…
Reference in New Issue
Block a user