dev: hello world

This commit is contained in:
vamsi 2022-11-19 19:51:26 +05:30
commit 6037fed3f4
145 changed files with 16848 additions and 0 deletions

19
layouts/AdminLayout.tsx Normal file
View file

@ -0,0 +1,19 @@
import React from "react";
// layouts
import Container from "layouts/Container";
// types
import type { Props } from "./types";
const AdminLayout: React.FC<Props> = ({ meta, children }) => {
return (
<Container meta={meta}>
<div className="w-full h-screen overflow-auto">
<>{children}</>
</div>
</Container>
);
};
export default AdminLayout;

71
layouts/Container.tsx Normal file
View file

@ -0,0 +1,71 @@
import Head from "next/head";
import { useRouter } from "next/router";
import {
SITE_NAME,
SITE_DESCRIPTION,
SITE_URL,
TWITTER_USER_NAME,
SITE_KEYWORDS,
SITE_TITLE,
} from "constants/seo/seo-variables";
// types
import type { Props } from "./types";
const Container = ({ meta, children }: Props) => {
const router = useRouter();
const image = meta?.image || "/site-image.png";
const title = meta?.title || SITE_TITLE;
const url = meta?.url || `${SITE_URL}${router.asPath}`;
const description = meta?.description || SITE_DESCRIPTION;
return (
<>
<Head>
<title>{title}</title>
<meta property="og:site_name" content={SITE_NAME} />
<meta property="og:title" content={title} />
<meta property="og:url" content={url} />
<meta name="description" content={description} />
<meta property="og:description" content={description} />
<meta name="keywords" content={SITE_KEYWORDS} />
<meta name="twitter:site" content={`@${TWITTER_USER_NAME}`} />
<meta
name="twitter:card"
content={image ? "summary_large_image" : "summary"}
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/favicon/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon/favicon-16x16.png"
/>
<link rel="manifest" href="/site.webmanifest.json" />
<link rel="shortcut icon" href="/favicon/favicon.ico" />
{image && (
<meta
property="og:image"
content={
image.startsWith("https://") ? image : `${SITE_URL}${image}`
}
/>
)}
</Head>
{children}
</>
);
};
export default Container;

21
layouts/DefaultLayout.tsx Normal file
View file

@ -0,0 +1,21 @@
import React from "react";
// layouts
import Container from "layouts/Container";
import DefaultTopBar from "layouts/Navbar/DefaultTopBar";
// types
import type { Props } from "./types";
const DefaultLayout: React.FC<Props> = ({ meta, children }) => {
return (
<Container meta={meta}>
<div className="w-full h-screen overflow-auto bg-gray-50">
{/* <DefaultTopBar /> */}
<>{children}</>
</div>
</Container>
);
};
export default DefaultLayout;

View file

@ -0,0 +1,35 @@
import React from "react";
// next js
import Link from "next/link";
// hooks
import useUser from "lib/hooks/useUser";
const DefaultTopBar: React.FC = () => {
const { user } = useUser();
return (
<div className="flex justify-between items-center px-4 h-16 sm:px-6 md:justify-start md:space-x-10 absolute top-0 w-full">
<div className="w-full flex items-center justify-between">
<div>
<Link href="/">
<a className="flex">
<span className="sr-only">Plane</span>
<h2 className="text-2xl font-semibold">
Plan<span className="text-indigo-600">e</span>
</h2>
</a>
</Link>
</div>
{user && (
<div>
<p className="text-sm text-gray-500">
logged in as {user.first_name}
</p>
</div>
)}
</div>
</div>
);
};
export default DefaultTopBar;

501
layouts/Navbar/Sidebar.tsx Normal file
View file

@ -0,0 +1,501 @@
// next
import Link from "next/link";
import { useRouter } from "next/router";
// react
import React, { useState } from "react";
// services
import useUser from "lib/hooks/useUser";
import userService from "lib/services/user.service";
// components
import CreateProjectModal from "components/project/CreateProjectModal";
// types
import { IUser } from "types";
// headless ui
import { Dialog, Disclosure, Menu, Transition } from "@headlessui/react";
// icons
import {
ArrowPathIcon,
Bars3Icon,
ChevronDownIcon,
Cog6ToothIcon,
HomeIcon,
ClipboardDocumentListIcon,
PlusIcon,
RectangleStackIcon,
UserGroupIcon,
UserIcon,
XMarkIcon,
InboxIcon,
ArrowLongLeftIcon,
} from "@heroicons/react/24/outline";
// constants
import { classNames } from "constants/common";
import { Spinner } from "ui";
import useTheme from "lib/hooks/useTheme";
import authenticationService from "lib/services/authentication.service";
const navigation = (projectId: string) => [
{
name: "Issues",
href: `/projects/${projectId}/issues`,
icon: RectangleStackIcon,
},
{
name: "Cycles",
href: `/projects/${projectId}/cycles`,
icon: ArrowPathIcon,
},
{
name: "Members",
href: `/projects/${projectId}/members`,
icon: UserGroupIcon,
},
{
name: "Settings",
href: `/projects/${projectId}/settings`,
icon: Cog6ToothIcon,
},
];
const navLinks = [
{
icon: HomeIcon,
name: "Home",
href: `/workspace`,
},
{
icon: ClipboardDocumentListIcon,
name: "Projects",
href: "/projects",
},
{
icon: RectangleStackIcon,
name: "My Issues",
href: "/me/my-issues",
},
{
icon: UserGroupIcon,
name: "Members",
href: "/workspace/members",
},
// {
// icon: InboxIcon,
// name: "Inbox",
// href: "#",
// },
{
icon: Cog6ToothIcon,
name: "Settings",
href: "/workspace/settings",
},
];
const userLinks = [
{
name: "My Profile",
href: "/me/profile",
},
{
name: "Workspace Invites",
href: "/invitations",
},
];
const Sidebar: React.FC = () => {
const [sidebarOpen, setSidebarOpen] = useState(false);
const [isCreateProjectModal, setCreateProjectModal] = useState(false);
const router = useRouter();
const { projects } = useUser();
const { projectId } = router.query;
const { workspaces, activeWorkspace, mutateUser } = useUser();
const { collapsed: sidebarCollapse, toggleCollapsed } = useTheme();
return (
<nav className="h-screen">
<CreateProjectModal isOpen={isCreateProjectModal} setIsOpen={setCreateProjectModal} />
<Transition.Root show={sidebarOpen} as={React.Fragment}>
<Dialog as="div" className="relative z-40 md:hidden" onClose={setSidebarOpen}>
<Transition.Child
as={React.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"
>
<div className="fixed inset-0 bg-gray-600 bg-opacity-75" />
</Transition.Child>
<div className="fixed inset-0 z-40 flex">
<Transition.Child
as={React.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"
>
<Dialog.Panel className="relative flex w-full max-w-xs flex-1 flex-col bg-white">
<Transition.Child
as={React.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-12 pt-2">
<button
type="button"
className="ml-1 flex h-10 w-10 items-center justify-center rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
onClick={() => setSidebarOpen(false)}
>
<span className="sr-only">Close sidebar</span>
<XMarkIcon className="h-6 w-6 text-white" aria-hidden="true" />
</button>
</div>
</Transition.Child>
<div className="h-0 flex-1 overflow-y-auto pt-5 pb-4">
<nav className="mt-5 space-y-1 px-2">
{projectId &&
navigation(projectId as string).map((item) => (
<Link href={item.href} key={item.name}>
<a
className={classNames(
item.href === router.asPath
? "bg-gray-100 text-gray-900"
: "text-gray-600 hover:bg-gray-50 hover:text-gray-900",
"group flex items-center px-2 py-2 text-base font-medium rounded-md"
)}
>
<item.icon
className={classNames(
item.href === router.asPath
? "text-gray-500"
: "text-gray-400 group-hover:text-gray-500",
"mr-4 flex-shrink-0 h-6 w-6"
)}
aria-hidden="true"
/>
{item.name}
</a>
</Link>
))}
</nav>
</div>
</Dialog.Panel>
</Transition.Child>
<div className="w-14 flex-shrink-0" />
</div>
</Dialog>
</Transition.Root>
<div
className={`${
sidebarCollapse ? "" : "w-auto md:w-64"
} hidden md:inset-y-0 md:flex md:flex-col h-full`}
>
<div className="h-full flex flex-1 flex-col border-r border-gray-200">
<div className="h-full flex flex-1 flex-col pt-5">
<div className="px-2">
<div className={`relative ${sidebarCollapse ? "flex" : "grid grid-cols-5 gap-1"}`}>
<Menu as="div" className="col-span-4 inline-block text-left w-full">
<div className="w-full">
<Menu.Button
className={`inline-flex justify-between items-center w-full rounded-md px-2 py-2 text-sm font-semibold text-gray-700 focus:outline-none ${
!sidebarCollapse ? "hover:bg-gray-50 border border-gray-300 shadow-sm" : ""
}`}
>
<span className="flex gap-x-1 items-center">
<p className="h-5 w-5 p-4 flex items-center justify-center bg-gray-500 text-white rounded uppercase">
{activeWorkspace?.name?.charAt(0) ?? "N"}
</p>
{!sidebarCollapse && (
<p className="truncate w-20 text-left ml-1">
{activeWorkspace?.name ?? "Loading..."}
</p>
)}
</span>
{!sidebarCollapse && (
<div className="flex-grow flex justify-end">
<ChevronDownIcon className="-mr-1 ml-2 h-5 w-5" aria-hidden="true" />
</div>
)}
</Menu.Button>
</div>
<Transition
as={React.Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="origin-top-left fixed max-w-[15rem] ml-2 left-0 mt-2 w-full rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none z-50">
<div className="p-1">
{workspaces ? (
<>
{workspaces.length > 0 ? (
workspaces.map((workspace: any) => (
<Menu.Item key={workspace.id}>
{({ active }) => (
<button
type="button"
onClick={() => {
mutateUser(
(prevData) => ({
...(prevData as IUser),
last_workspace_id: workspace.id,
}),
false
);
userService
.updateUser({
last_workspace_id: workspace?.id,
})
.then((res) => {
router.push("/workspace");
})
.catch((err) => console.log);
}}
className={`${
active ? "bg-theme text-white" : "text-gray-900"
} group flex w-full items-center rounded-md p-2 text-sm`}
>
{workspace.name}
</button>
)}
</Menu.Item>
))
) : (
<p>No workspace found!</p>
)}
<Menu.Item>
{(active) => (
<Link href="/create-workspace">
<a className="flex items-center gap-x-1 p-2 w-full text-left text-gray-900 hover:bg-theme hover:text-white rounded-md text-sm">
<PlusIcon className="w-5 h-5" />
<span>Create Workspace</span>
</a>
</Link>
)}
</Menu.Item>
</>
) : (
<div className="w-full flex justify-center">
<Spinner />
</div>
)}
</div>
</Menu.Items>
</Transition>
</Menu>
{!sidebarCollapse && (
<Menu as="div" className="inline-block text-left w-full">
<div className="h-full w-full">
<Menu.Button className="grid place-items-center h-full w-full rounded-md shadow-sm px-2 py-2 bg-white border border-gray-300 text-gray-700 hover:bg-gray-50 focus:outline-none">
<UserIcon className="h-5 w-5" />
</Menu.Button>
</div>
<Transition
as={React.Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="origin-top-right absolute left-0 mt-2 w-full rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none z-50">
<div className="p-1">
{userLinks.map((item) => (
<Menu.Item key={item.name} as="div">
{(active) => (
<Link href={item.href}>
<a className="flex items-center gap-x-1 p-2 w-full text-left text-gray-900 hover:bg-theme hover:text-white rounded-md text-sm">
{item.name}
</a>
</Link>
)}
</Menu.Item>
))}
<Menu.Item as="div">
<button
type="button"
className="flex items-center gap-x-1 p-2 w-full text-left text-gray-900 hover:bg-theme hover:text-white rounded-md text-sm"
onClick={async () => {
await authenticationService
.signOut({
refresh_token: authenticationService.getRefreshToken(),
})
.then((response) => {
console.log("user signed out", response);
})
.catch((error) => {
console.log("Failed to sign out", error);
})
.finally(() => {
mutateUser();
router.push("/signin");
});
}}
>
Sign Out
</button>
</Menu.Item>
</div>
</Menu.Items>
</Transition>
</Menu>
)}
</div>
<div className="mt-3 flex-1 space-y-1 bg-white">
{navLinks.map((link, index) => (
<Link key={index} href={link.href}>
<a
className={`${
link.href === router.asPath ? "bg-theme text-white" : "hover:bg-indigo-100"
} group flex items-center gap-3 px-2 py-2 text-xs font-medium rounded-md ${
sidebarCollapse ? "justify-center" : ""
}`}
>
<link.icon
className={`${
link.href === router.asPath ? "text-white" : ""
} flex-shrink-0 h-4 w-4`}
aria-hidden="true"
/>
{!sidebarCollapse && link.name}
</a>
</Link>
))}
</div>
</div>
<div
className={`flex flex-col px-2 pt-5 pb-3 mt-3 space-y-2 bg-gray-50 h-full overflow-y-auto ${
sidebarCollapse ? "rounded-xl" : "rounded-t-3xl"
}`}
>
{projects ? (
<>
{projects.length > 0 ? (
projects.map((project) => (
<Disclosure key={project?.id} defaultOpen={projectId === project?.id}>
<Disclosure.Button
className={`w-full flex items-center gap-2 font-medium rounded-md p-2 text-sm ${
sidebarCollapse ? "justify-center" : ""
}`}
>
<span className="bg-gray-700 text-white rounded h-7 w-7 grid place-items-center uppercase">
{project?.name.charAt(0)}
</span>
{!sidebarCollapse && project?.name}
</Disclosure.Button>
<Transition
enter="transition duration-100 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0"
>
<Disclosure.Panel
className={`${
sidebarCollapse ? "" : "ml-[2.25rem]"
} flex flex-col gap-y-1`}
>
{navigation(project?.id).map((item) => (
<Link key={item.name} href={item.href}>
<a
className={classNames(
item.href === router.asPath
? "bg-gray-200 text-gray-900"
: "text-gray-500 hover:bg-gray-100 hover:text-gray-900",
"group flex items-center px-2 py-2 text-xs font-medium rounded-md",
sidebarCollapse ? "justify-center" : ""
)}
>
<item.icon
className={classNames(
item.href === router.asPath
? "text-gray-900"
: "text-gray-500 group-hover:text-gray-900",
"flex-shrink-0 h-4 w-4",
!sidebarCollapse ? "mr-3" : ""
)}
aria-hidden="true"
/>
{!sidebarCollapse && item.name}
</a>
</Link>
))}
</Disclosure.Panel>
</Transition>
</Disclosure>
))
) : (
<div className="text-center space-y-3">
<h4 className="text-gray-700 text-sm">You don{"'"}t have any project yet</h4>
<button
type="button"
className="group flex justify-center items-center gap-2 w-full rounded-md p-2 text-sm bg-theme text-white"
onClick={() => setCreateProjectModal(true)}
>
<PlusIcon className="h-5 w-5" />
Create Project
</button>
</div>
)}
</>
) : (
<div className="w-full flex justify-center">
<Spinner />
</div>
)}
</div>
<div className="px-2 py-2 bg-gray-50 w-full self-baseline">
<button
type="button"
className={`flex items-center gap-3 px-2 py-2 text-xs font-medium rounded-md text-gray-500 hover:bg-gray-100 hover:text-gray-900 w-full ${
sidebarCollapse ? "justify-center" : ""
}`}
onClick={() => toggleCollapsed()}
>
<ArrowLongLeftIcon
className={`h-4 w-4 text-gray-500 group-hover:text-gray-900 flex-shrink-0 duration-300 ${
sidebarCollapse ? "rotate-180" : ""
}`}
/>
{!sidebarCollapse && "Collapse"}
</button>
</div>
</div>
</div>
</div>
<div className="sticky top-0 z-10 bg-white pl-1 pt-1 sm:pl-3 sm:pt-3 md:hidden">
<button
type="button"
className="-ml-0.5 -mt-0.5 inline-flex h-12 w-12 items-center justify-center rounded-md text-gray-500 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500"
onClick={() => setSidebarOpen(true)}
>
<span className="sr-only">Open sidebar</span>
<Bars3Icon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
</nav>
);
};
export default Sidebar;

27
layouts/ProjectLayout.tsx Normal file
View file

@ -0,0 +1,27 @@
import React, { useState } from "react";
// components
import CreateProjectModal from "components/project/CreateProjectModal";
// layouts
import AdminLayout from "layouts/AdminLayout";
// types
import type { Props } from "./types";
// components
import Sidebar from "./Navbar/Sidebar";
const ProjectLayouts: React.FC<Props> = ({ children, meta }) => {
const [isOpen, setIsOpen] = useState(false);
return (
<AdminLayout meta={meta}>
<CreateProjectModal isOpen={isOpen} setIsOpen={setIsOpen} />
<div className="h-full w-full overflow-x-hidden relative flex">
<Sidebar />
<main className="h-full w-full mx-auto min-w-0 pb-6 overflow-y-hidden">
<div className="h-full w-full px-8 py-6 overflow-auto">{children}</div>
</main>
</div>
</AdminLayout>
);
};
export default ProjectLayouts;

11
layouts/types.d.ts vendored Normal file
View file

@ -0,0 +1,11 @@
export type Meta = {
title?: string | null;
description?: string | null;
image?: string | null;
url?: string | null;
};
export type Props = {
meta?: Meta;
children: React.ReactNode;
};