mirror of https://github.com/ctk-hq/ctk
task: expose projects, folder restructure, minor fixes
parent
74b90ae4ff
commit
59ea695935
@ -0,0 +1,91 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { PencilIcon, TrashIcon } from "@heroicons/react/outline";
|
||||||
|
import { truncateStr } from "../../utils";
|
||||||
|
import { IProject } from "../../types";
|
||||||
|
import ModalConfirmDelete from "../../components/Modal/ConfirmDelete";
|
||||||
|
import { useDeleteProject } from "../../hooks/useProject";
|
||||||
|
|
||||||
|
interface IPreviewBlockProps {
|
||||||
|
project: IProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PreviewBlock = (props: IPreviewBlockProps) => {
|
||||||
|
const { project } = props;
|
||||||
|
const [isHovering, setIsHovering] = useState(false);
|
||||||
|
const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false);
|
||||||
|
const objId = project.id;
|
||||||
|
const mutation = useDeleteProject(project.uuid);
|
||||||
|
|
||||||
|
const handleMouseOver = () => {
|
||||||
|
setIsHovering(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseLeave = () => {
|
||||||
|
setIsHovering(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDelete = () => {
|
||||||
|
setShowDeleteConfirmModal(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDeleteConfirmed = () => {
|
||||||
|
mutation.mutate();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
onMouseOver={handleMouseOver}
|
||||||
|
onMouseLeave={handleMouseLeave}
|
||||||
|
key={project.id}
|
||||||
|
className={`
|
||||||
|
relative
|
||||||
|
rounded-lg
|
||||||
|
dark:bg-gray-900
|
||||||
|
bg-gray-100
|
||||||
|
px-6
|
||||||
|
py-5
|
||||||
|
shadow-sm
|
||||||
|
flex
|
||||||
|
items-center
|
||||||
|
space-x-3
|
||||||
|
hover:border-gray-400
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
{truncateStr(project.name, 25)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isHovering &&
|
||||||
|
<div className="flex space-x-1 absolute top-2 right-2">
|
||||||
|
<button
|
||||||
|
onClick={() => onDelete()}
|
||||||
|
className="flex justify-center items-center p-2 hover:bg-gray-100 shadow bg-white rounded-md"
|
||||||
|
>
|
||||||
|
<TrashIcon className="w-3 h-3 text-red-500 hover:text-red-600" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
to={`/projects/${project.uuid}`}
|
||||||
|
className="flex justify-center items-center p-2 hover:bg-gray-100 shadow bg-white rounded-md"
|
||||||
|
>
|
||||||
|
<PencilIcon className="w-3 h-3 text-gray-500 hover:text-gray-600" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{showDeleteConfirmModal &&
|
||||||
|
<ModalConfirmDelete
|
||||||
|
onConfirm={() => onDeleteConfirmed()}
|
||||||
|
onHide={() => {
|
||||||
|
setShowDeleteConfirmModal(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PreviewBlock;
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { IProject } from "../../types";
|
||||||
|
import Spinner from "../../components/global/Spinner";
|
||||||
|
import PreviewBlock from "./PreviewBlock";
|
||||||
|
import { useProjects } from "../../hooks/useProjects";
|
||||||
|
|
||||||
|
const Projects = () => {
|
||||||
|
const { data, error, isFetching } = useProjects();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="md:pl-16 flex flex-col flex-1">
|
||||||
|
<main>
|
||||||
|
<div className="py-6">
|
||||||
|
<div className="flex justify-between px-4 sm:px-6 md:px-8">
|
||||||
|
<h1 className="text-2xl font-semibold dark:text-white text-gray-900">Projects</h1>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
className="btn-util text-white bg-blue-600 hover:bg-blue-700 sm:w-auto"
|
||||||
|
to="/projects/new"
|
||||||
|
>
|
||||||
|
<span>New</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-4 sm:px-6 md:px-8">
|
||||||
|
{isFetching &&
|
||||||
|
<div className="flex justify-center items-center mx-auto mt-10">
|
||||||
|
<Spinner className="w-6 h-6 text-blue-600" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{!isFetching &&
|
||||||
|
<div className="py-4">
|
||||||
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{(data.results.length > 0) &&
|
||||||
|
data.results.map((project: IProject) => {
|
||||||
|
return (
|
||||||
|
<div key={`${project.uuid}`}>
|
||||||
|
<PreviewBlock
|
||||||
|
project={project}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{(data.results.length === 0) &&
|
||||||
|
<div className="text-center">
|
||||||
|
<h3 className="mt-12 text-sm font-medium text-gray-900 dark:text-white">Nothing here</h3>
|
||||||
|
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">Get started by creating a project.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Projects;
|
||||||
@ -1,132 +0,0 @@
|
|||||||
import { Fragment } from "react";
|
|
||||||
import { useLocation } from "react-router-dom";
|
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
|
||||||
import { DatabaseIcon, TemplateIcon, XIcon } from "@heroicons/react/outline";
|
|
||||||
import UserMenu from "./UserMenu";
|
|
||||||
import Logo from "./logo";
|
|
||||||
|
|
||||||
interface ISideBarProps {
|
|
||||||
state: any;
|
|
||||||
sidebarOpen: boolean;
|
|
||||||
setSidebarOpen: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function SideBar(props: ISideBarProps) {
|
|
||||||
const { pathname } = useLocation();
|
|
||||||
const { state, sidebarOpen, setSidebarOpen } = props;
|
|
||||||
const navigation = [
|
|
||||||
{ name: "Templates", href: "/", icon: TemplateIcon, current: ((pathname === "/" || pathname.includes("templates")) ? true : false) },
|
|
||||||
{ name: "Connectors", href: "/connectors", icon: DatabaseIcon, current: (pathname.includes("connectors") ? true : false) }
|
|
||||||
];
|
|
||||||
const classNames = (...classes: any[]) => {
|
|
||||||
return classes.filter(Boolean).join(" ")
|
|
||||||
};
|
|
||||||
const userName = state.user ? state.user.username : "";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Transition.Root show={sidebarOpen} as={Fragment}>
|
|
||||||
<Dialog as="div" className="fixed inset-0 flex z-40 md:hidden" onClose={setSidebarOpen}>
|
|
||||||
<Transition.Child
|
|
||||||
as={Fragment}
|
|
||||||
enter="transition-opacity ease-linear duration-300"
|
|
||||||
enterFrom="opacity-0"
|
|
||||||
enterTo="opacity-100"
|
|
||||||
leave="transition-opacity ease-linear duration-300"
|
|
||||||
leaveFrom="opacity-100"
|
|
||||||
leaveTo="opacity-0"
|
|
||||||
>
|
|
||||||
<Dialog.Overlay className="fixed inset-0 bg-gray-700 bg-opacity-50" />
|
|
||||||
</Transition.Child>
|
|
||||||
<Transition.Child
|
|
||||||
as={Fragment}
|
|
||||||
enter="transition ease-in-out duration-300 transform"
|
|
||||||
enterFrom="-translate-x-full"
|
|
||||||
enterTo="translate-x-0"
|
|
||||||
leave="transition ease-in-out duration-300 transform"
|
|
||||||
leaveFrom="translate-x-0"
|
|
||||||
leaveTo="-translate-x-full"
|
|
||||||
>
|
|
||||||
<div className="relative flex-1 flex flex-col max-w-xs w-full pt-5 pb-4 bg-blue-700">
|
|
||||||
<Transition.Child
|
|
||||||
as={Fragment}
|
|
||||||
enter="ease-in-out duration-300"
|
|
||||||
enterFrom="opacity-0"
|
|
||||||
enterTo="opacity-100"
|
|
||||||
leave="ease-in-out duration-300"
|
|
||||||
leaveFrom="opacity-100"
|
|
||||||
leaveTo="opacity-0"
|
|
||||||
>
|
|
||||||
<div className="absolute top-0 right-0 -mr-7 pt-2">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="ml-1 flex items-center justify-center h-5 w-5 rounded-full"
|
|
||||||
onClick={() => setSidebarOpen(false)}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Close sidebar</span>
|
|
||||||
<XIcon className="h-6 w-6 text-white" aria-hidden="true" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</Transition.Child>
|
|
||||||
|
|
||||||
<div className="flex-shrink-0 flex items-center px-4">
|
|
||||||
<Logo className="w-5 h-5" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-5 flex-1 h-0 overflow-y-auto">
|
|
||||||
<nav className="px-2 space-y-1">
|
|
||||||
{navigation.map((item) => (
|
|
||||||
<a
|
|
||||||
key={item.name}
|
|
||||||
href={item.href}
|
|
||||||
className={classNames(
|
|
||||||
item.current ? "bg-blue-800 text-white" : "text-blue-100 hover:bg-blue-600",
|
|
||||||
"group flex items-center px-2 py-2 text-base font-medium rounded-md"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<item.icon className="mr-4 flex-shrink-0 h-6 w-6" aria-hidden="true" />
|
|
||||||
{item.name}
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<UserMenu username={userName} current={pathname.includes("profile")} />
|
|
||||||
</div>
|
|
||||||
</Transition.Child>
|
|
||||||
|
|
||||||
<div className="flex-shrink-0 w-14" aria-hidden="true">
|
|
||||||
{/* Dummy element to force sidebar to shrink to fit close icon */}
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
</Transition.Root>
|
|
||||||
|
|
||||||
<div className="hidden md:flex md:w-56 md:flex-col md:fixed md:inset-y-0">
|
|
||||||
<div className="flex flex-col flex-grow pt-5 bg-blue-700 overflow-y-auto">
|
|
||||||
<div className="flex items-center flex-shrink-0 px-4">
|
|
||||||
<Logo className="w-5 h-5" />
|
|
||||||
</div>
|
|
||||||
<div className="mt-5 flex-1 flex flex-col">
|
|
||||||
<nav className="flex-1 px-2 pb-4 space-y-1">
|
|
||||||
{navigation.map((item) => (
|
|
||||||
<a
|
|
||||||
key={item.name}
|
|
||||||
href={item.href}
|
|
||||||
className={classNames(
|
|
||||||
item.current ? "bg-blue-800 text-white" : "text-blue-100 hover:bg-blue-600",
|
|
||||||
"group flex items-center px-2 py-2 text-sm font-medium rounded-md"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<item.icon className="mr-3 flex-shrink-0 h-5 w-5" aria-hidden="true" />
|
|
||||||
{item.name}
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<UserMenu username={userName} current={pathname.includes("profile")} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
import Spinner from "./Spinner";
|
||||||
|
|
||||||
|
|
||||||
|
interface IPaginationProps {
|
||||||
|
defaultCurrent: number;
|
||||||
|
defaultPageSize: number;
|
||||||
|
onChange: any;
|
||||||
|
total: number;
|
||||||
|
loading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Pagination = (props: IPaginationProps) => {
|
||||||
|
const { defaultCurrent, onChange, total, loading } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="text-center">
|
||||||
|
<span className="block mb-5 text-sm">{`showing ${defaultCurrent} of ${total}`}</span>
|
||||||
|
<button
|
||||||
|
className="inline-flex items-center px-4 py-2 border border-transparent
|
||||||
|
text-xs font-medium rounded-full shadow-sm text-white bg-blue-600 hover:bg-blue-700"
|
||||||
|
type="button"
|
||||||
|
disabled={loading}
|
||||||
|
onClick={() => onChange()}
|
||||||
|
>
|
||||||
|
{loading ? <Spinner className="w-4 h-4" /> : <span>load more</span>}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Pagination;
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { SearchIcon } from "@heroicons/react/solid";
|
||||||
|
|
||||||
|
interface ISearchProps {
|
||||||
|
onSearchChange: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Search = (props: ISearchProps) => {
|
||||||
|
const { onSearchChange } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex-1 flex">
|
||||||
|
<form className="w-full flex md:ml-0" autoComplete="off" method="post" action="">
|
||||||
|
<input autoComplete="false" name="hidden" type="text" className="hidden" />
|
||||||
|
<label htmlFor="search-field" className="sr-only">
|
||||||
|
Search
|
||||||
|
</label>
|
||||||
|
<div className="relative w-full text-gray-400 focus-within:text-gray-400">
|
||||||
|
<div className="absolute inset-y-0 left-0 flex items-center pointer-events-none">
|
||||||
|
<SearchIcon className="h-5 w-5" aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
id="search-field"
|
||||||
|
className="dark:bg-gray-800 block w-full h-full pl-8 pr-3 py-2 border-transparent dark:text-white text-gray-900 placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-0 focus:border-transparent sm:text-sm"
|
||||||
|
placeholder="Search"
|
||||||
|
type="input"
|
||||||
|
name="search"
|
||||||
|
onChange={onSearchChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Search;
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
import { BookOpenIcon } from "@heroicons/react/outline";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import UserMenu from "./UserMenu";
|
||||||
|
import Logo from "./logo";
|
||||||
|
|
||||||
|
interface ISideBarProps {
|
||||||
|
state: any;
|
||||||
|
isAuthenticated: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SideBar(props: ISideBarProps) {
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
const { state, isAuthenticated } = props;
|
||||||
|
const projRegex = /\/projects\/?$/;
|
||||||
|
const navigation = [{
|
||||||
|
name: "Projects",
|
||||||
|
href: "/projects",
|
||||||
|
icon: BookOpenIcon,
|
||||||
|
current: (pathname.match(projRegex) ? true : false)
|
||||||
|
}];
|
||||||
|
const classNames = (...classes: any[]) => {
|
||||||
|
return classes.filter(Boolean).join(" ")
|
||||||
|
};
|
||||||
|
const userName = state.user ? state.user.username : "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="md:flex md:w-16 md:flex-col md:fixed md:inset-y-0">
|
||||||
|
<div className="flex flex-col flex-grow pt-5 bg-blue-700 overflow-y-auto">
|
||||||
|
<div className="flex items-center flex-shrink-0 mx-auto">
|
||||||
|
<Link to={isAuthenticated ? "/" : "projects/new"}>
|
||||||
|
<Logo className="" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 flex-1 flex flex-col">
|
||||||
|
<nav className="flex-1 px-2 pb-4 space-y-1">
|
||||||
|
{navigation.map((item) => (
|
||||||
|
<a
|
||||||
|
key={item.name}
|
||||||
|
href={item.href}
|
||||||
|
className={classNames(
|
||||||
|
item.current ? "bg-blue-800 text-white" : "text-blue-100 hover:bg-blue-600",
|
||||||
|
"group flex items-center justify-center px-2 py-2 text-sm font-medium rounded-md"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<item.icon className="mr-3 md:mr-0 flex-shrink-0 h-5 w-5" aria-hidden="true" />
|
||||||
|
<span className="md:hidden">
|
||||||
|
{item.name}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UserMenu username={userName} current={pathname.includes("profile")} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
interface ILogoProps {
|
||||||
|
className: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Logo = (props: ILogoProps) => {
|
||||||
|
const { className } = props;
|
||||||
|
return (
|
||||||
|
<svg width="28px" height="28px" viewBox="0 0 152 152" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>Nuxx</title>
|
||||||
|
<g id="Page-1" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
|
||||||
|
<g id="Logo" transform="translate(-179.000000, -225.000000)">
|
||||||
|
<g id="nuxx-logo-copy" transform="translate(167.000000, 200.000000)">
|
||||||
|
<g id="nuxx-logo" transform="translate(98.500000, 95.000000) rotate(-60.000000) translate(-98.500000, -95.000000) translate(33.000000, 20.000000)">
|
||||||
|
<polygon id="Polygon" fillOpacity="0.15" fill="#000000" points="44 0 87.3012702 25 87.3012702 75 44 100 0.698729811 75 0.698729811 25"></polygon>
|
||||||
|
<polygon id="Polygon-Copy" fillOpacity="0.15" fill="#000000" points="87 25 130.30127 50 130.30127 100 87 125 43.6987298 100 43.6987298 50"></polygon>
|
||||||
|
<polygon id="Polygon-Copy-2" fillOpacity="0.15" fill="#000000" points="44 50 87.3012702 75 87.3012702 125 44 150 0.698729811 125 0.698729811 75"></polygon>
|
||||||
|
<text id="n" transform="translate(61.705647, 69.842201) rotate(30.000000) translate(-61.705647, -69.842201) " fontFamily="Kefa-Bold, Kefa" fontSize="71" fontWeight="bold" letterSpacing="0.492226294" fill="#FFFFFF">
|
||||||
|
<tspan x="39.6707155" y="94.8422015">n</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Logo;
|
||||||
@ -1,20 +0,0 @@
|
|||||||
interface ILogoProps {
|
|
||||||
className: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Logo = (props: ILogoProps) => {
|
|
||||||
const { className } = props;
|
|
||||||
return (
|
|
||||||
<svg width="689" height="689" viewBox="0 0 689 689" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
|
|
||||||
<path opacity="0.8" d="M191.04 268.58H0.154419L0.154419 459.465H191.04V268.58Z" fill="white" />
|
|
||||||
<path opacity="0.8" d="M191.04 497.306H0.154419L0.154419 688.192H191.04V497.306Z" fill="white" />
|
|
||||||
<path opacity="0.8" d="M191.04 39.8536L0.154419 39.8536L0.154419 230.739H191.04V39.8536Z" fill="#4F95FF" />
|
|
||||||
<path opacity="0.8" d="M419.766 268.58H228.881V459.465H419.766V268.58Z" fill="#4F95FF" />
|
|
||||||
<path opacity="0.8" d="M419.766 497.306H228.881V688.192H419.766V497.306Z" fill="#4F95FF" />
|
|
||||||
<path opacity="0.8" d="M648.493 497.306H457.607V688.192H648.493V497.306Z" fill="white" />
|
|
||||||
<path opacity="0.8" d="M688.084 135.105L553.109 0.130157L418.134 135.105L553.109 270.081L688.084 135.105Z" fill="white" />
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Logo;
|
|
||||||
@ -1,3 +1,3 @@
|
|||||||
export const API_SERVER_URL = process.env.REACT_APP_API_SERVER;
|
export const API_SERVER_URL = process.env.REACT_APP_API_SERVER;
|
||||||
export const PROJECTS_FETCH_LIMIT = 300;
|
export const PROJECTS_FETCH_LIMIT = 300;
|
||||||
export const LOCAL_STORAGE = 'CtkLocalStorage';
|
export const LOCAL_STORAGE = 'NuxxLocalStorage';
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
import axios from "axios"
|
||||||
|
import { useQuery } from "react-query";
|
||||||
|
import { API_SERVER_URL, PROJECTS_FETCH_LIMIT } from "../constants";
|
||||||
|
import { getLocalStorageJWTKeys } from "../utils";
|
||||||
|
|
||||||
|
const fetchProjects = async () => {
|
||||||
|
const jwtKeys = getLocalStorageJWTKeys();
|
||||||
|
|
||||||
|
const response = await axios({
|
||||||
|
method: 'get',
|
||||||
|
url: `${API_SERVER_URL}/projects/`,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": `Bearer ${jwtKeys.access_token}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useProjects = () => {
|
||||||
|
return useQuery(
|
||||||
|
["projects"],
|
||||||
|
async () => {
|
||||||
|
return await fetchProjects();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
staleTime: Infinity
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,62 +0,0 @@
|
|||||||
import { IProjectPayload } from "../types";
|
|
||||||
import { API_SERVER_URL, PROJECTS_FETCH_LIMIT } from "../constants";
|
|
||||||
import { getLocalStorageJWTKeys } from "./utils";
|
|
||||||
|
|
||||||
export const projectHttpCreate = (data: string) => {
|
|
||||||
//const jwtKeys = getLocalStorageJWTKeys();
|
|
||||||
return fetch(`${API_SERVER_URL}/projects/`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
//"Authorization": `Bearer ${jwtKeys.access_token}`
|
|
||||||
},
|
|
||||||
body: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const projectHttpUpdate = (uuid: string, data: string) => {
|
|
||||||
//const jwtKeys = getLocalStorageJWTKeys();
|
|
||||||
return fetch(`${API_SERVER_URL}/projects/${uuid}/`, {
|
|
||||||
method: "PUT",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
//"Authorization": `Bearer ${jwtKeys.access_token}`
|
|
||||||
},
|
|
||||||
body: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const projectHttpDelete = (uuid: number) => {
|
|
||||||
const jwtKeys = getLocalStorageJWTKeys();
|
|
||||||
return fetch(`${API_SERVER_URL}/projects/${uuid}/`, {
|
|
||||||
method: "DELETE",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Authorization": `Bearer ${jwtKeys.access_token}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const projectsHttpGet = (offset: number) => {
|
|
||||||
const jwtKeys = getLocalStorageJWTKeys();
|
|
||||||
let endpoint = `${API_SERVER_URL}/projects/?limit=${PROJECTS_FETCH_LIMIT}&offset=${offset}`;
|
|
||||||
|
|
||||||
return fetch(endpoint, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Authorization": `Bearer ${jwtKeys.access_token}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const projectHttpGet = (uuid: string) => {
|
|
||||||
//const jwtKeys = getLocalStorageJWTKeys();
|
|
||||||
return fetch(`${API_SERVER_URL}/projects/${uuid}/`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
//"Authorization": `Bearer ${jwtKeys.access_token}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue