Skip to content

Commit

Permalink
features enable/disable functionality added (#1275)
Browse files Browse the repository at this point in the history
* general page added

* features enable/disable functionality added

* crown icon added

* import path fixed

* workspace route fix
  • Loading branch information
shahrukh802 committed Jul 18, 2024
1 parent f404e29 commit 3c9cfb5
Show file tree
Hide file tree
Showing 25 changed files with 628 additions and 4,096 deletions.
4 changes: 4 additions & 0 deletions client/app/(ee)/settings/logs/logs-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Card from "@/components/card";
import LogsTable from "components/LogsTable/index";
import { LogData } from "./logs-interface";
import Pagination from "@/components/pagination";
import usePermissionCheck from "@/hooks/useCheckFeature";

interface IProps {
logs: LogData[];
Expand All @@ -14,7 +15,10 @@ const LogsCard = ({ logs, logs_count }: IProps) => {
const [page, setPage] = useState(1);
const itemsPerPage = 15;

usePermissionCheck();

const totalPages = Math.ceil(logs_count / itemsPerPage);

return (
<Card extra="w-full py-2 px-5">
<LogsTable data={logs} />
Expand Down
2 changes: 1 addition & 1 deletion client/app/(ee)/settings/logs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default async function Logs() {
return (
<div className="w-full h-full overflow-y-auto custom-scroll mt-5 px-2 md:px-4">
<h1 className="text-2xl font-bold dark:text-white mb-10">Logs</h1>
<LogsCard logs={data?.logs} logs_count={data?.logs_count} />
<LogsCard logs={data?.logs || []} logs_count={data?.logs_count} />
</div>
);
}
12 changes: 12 additions & 0 deletions client/app/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Loader } from "@/components/loader/Loader";
import React from "react";

const Loading = () => {
return (
<div className="flex items-center justify-center w-full h-full mx-auto">
<Loader />
</div>
);
};

export default Loading;
158 changes: 158 additions & 0 deletions client/app/settings/general/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
"use client";
import React, { useEffect, useState } from "react";
import { Switch } from "@/components/ui/switch";
import { Loader } from "@/components/loader/Loader";
import { useGetMe, useUpdateUserRoutes } from "@/hooks/useUsers";
import { toast } from "react-toastify";
import ConfirmationDialog from "@/components/ConfirmationDialog";
import AppTooltip from "@/components/AppTooltip";
import { revalidateLogs } from "@/lib/actions";
import { useQueryClient } from "@tanstack/react-query";
import { FaCrown } from "react-icons/fa";

const GeneralPage = () => {
const [routes, setRoutes] = useState([]);
const { data: userResponse, isLoading } = useGetMe();
const { mutateAsync: updateUserRoutes, isPending } = useUpdateUserRoutes();
const [isModalOpen, setIsModalOpen] = useState(false);
const [feature, setFeature] = useState({
routeName: "",
featureName: null,
});
const queryClient = useQueryClient();

useEffect(() => {
if (userResponse) {
setRoutes(userResponse?.data?.features?.routes || []);
}
}, [userResponse]);

const handleToggle = (routeName, featureName) => {
let rName: string, fName: string;
if (!routeName && !featureName) {
rName = feature.routeName;
fName = feature.featureName;
} else {
rName = routeName;
fName = featureName;
}
const updatedRoutes = routes.map((route) => {
if (route.name === rName) {
if (fName) {
route.features = route.features.map((feature) =>
feature.name === fName
? { ...feature, enabled: !feature.enabled }
: feature
);
} else {
route.enabled = !route.enabled;
}
}
return route;
});

const body = {
routes: updatedRoutes,
};
setRoutes(updatedRoutes);
updateUserRoutes(body, {
onSuccess() {
queryClient.invalidateQueries({ queryKey: ["useGetMe"] });
revalidateLogs();
setIsModalOpen(false);
},
onError(error: any) {
toast.error(
error?.response?.data?.message
? error?.response?.data?.message
: error.message
);
},
});
};

return (
<>
<div className="w-full h-full overflow-y-auto custom-scroll mt-5 px-2 md:px-4">
<h1 className="text-2xl font-bold dark:text-white mb-10">General</h1>
{isLoading ? (
<Loader />
) : (
<div className="px-2 md:px-10">
{routes?.map((route) => (
<>
{route?.isEnterprise && (
<div className="mt-5" key={route.name}>
<div className="flex gap-5 mb-5">
<h2 className="text-xl font-semibold dark:text-white">
{route.name}
</h2>
</div>
<div className="flex items-center gap-5">
<div className="flex items-center gap-2">
<p className="dark:text-white">
{route.enabled ? "Disable" : "Enable"}{" "}
{route.name.toLowerCase()}
</p>
{route.isEnterprise && (
<AppTooltip text="Enterprise Feature">
<FaCrown />
</AppTooltip>
)}
</div>
<Switch
name={route.name}
checked={route.enabled}
onCheckedChange={() => {
if (!route.enabled) {
setIsModalOpen(true);
setFeature({
routeName: route.name,
featureName: null,
});
} else {
handleToggle(route.name, null);
}
}}
/>
</div>
{/* Will add features in the future */}
{/* {route.features.map((feature) => (
<div
className="flex items-center gap-5"
key={feature.name}
>
<p className="dark:text-white">
Toggle {feature.name.toLowerCase()}
</p>
<Switch
name={`${route.name}-${feature.name}`}
checked={feature.enabled}
onCheckedChange={() =>
handleToggle(route.name, feature.name)
}
/>
</div>
))} */}
</div>
)}
</>
))}
</div>
)}
</div>
{isModalOpen && (
<ConfirmationDialog
text={`Are you sure you want to enable enterprise feature?`}
onCancel={() => {
setIsModalOpen(false);
}}
onSubmit={() => handleToggle(null, null)}
isLoading={isPending}
/>
)}
</>
);
};

export default GeneralPage;
20 changes: 3 additions & 17 deletions client/app/settings/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
"use client";
import React from "react";
import VerticalLineSeperator from "components/VerticalLineSeperator";
import Drawer from "react-modern-drawer";
import "react-modern-drawer/dist/index.css";
import { useAppStore } from "store";
import Navbar from "@/components/Navbar";
import SettingsLeftBar from "@/components/SettingsLayout/LeftBar";
import LeftBarDrawer from "@/components/SettingsLayout/LeftBarDrawer";

export default function SettingsLayout({
export default async function SettingsLayout({
children,
}: {
children: React.ReactNode;
}) {
const isSideBarOpen = useAppStore((state) => state.isSideBarOpen);
const setIsSidebarOpen = useAppStore((state) => state.setIsSidebarOpen);

return (
<>
<div className="flex flex-col h-screen">
Expand All @@ -30,15 +24,7 @@ export default function SettingsLayout({
<div className="flex-1 w-full">{children}</div>
</div>
</div>
<Drawer
open={isSideBarOpen}
onClose={() => setIsSidebarOpen(false)}
direction="left"
className=""
zIndex={1000}
>
<SettingsLeftBar isMobileView={true} />
</Drawer>
<LeftBarDrawer />
</>
);
}
12 changes: 12 additions & 0 deletions client/app/settings/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Loader } from "@/components/loader/Loader";
import React from "react";

const Loading = () => {
return (
<div className="flex items-center justify-center w-full h-full mx-auto">
<Loader />
</div>
);
};

export default Loading;
45 changes: 16 additions & 29 deletions client/components/SettingsLayout/LeftBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,16 @@ import React from "react";
import { usePathname } from "next/navigation";
import Link from "next/link";
import { useAppStore } from "store";
import { useGetMe } from "@/hooks/useUsers";

interface IProps {
isMobileView?: boolean;
}

const routes = [
{
name: "Datasets",
url: "/settings/datasets",
isAdmin: true,
},
{
name: "Logs",
url: "/settings/logs",
isAdmin: true,
},
{
name: "Workspaces",
url: "/settings/workspaces",
isAdmin: true,
},
];

const SettingsLeftBar = ({ isMobileView = false }: IProps) => {
const setIsSidebarOpen = useAppStore((state) => state.setIsSidebarOpen);
const { data: userResponse } = useGetMe();

const pathname = usePathname();
const closeSidebar = () => {
setIsSidebarOpen(false);
Expand All @@ -52,17 +37,19 @@ const SettingsLeftBar = ({ isMobileView = false }: IProps) => {
Settings
</h2>
</div>
{routes?.map((route, index) => (
<Link href={route.url} key={index}>
<div
onClick={() => closeSidebar()}
key={index}
className={`px-4 py-1 dark:text-white text-base truncate cursor-pointer hover:underline ${
route.url === pathname ? "font-bold" : "font-light"
}`}
>
{route.name}
</div>
{userResponse?.data?.features?.routes?.map((route, index) => (
<Link href={route.path} key={index}>
{route.enabled && (
<div
onClick={() => closeSidebar()}
key={index}
className={`px-4 py-1 dark:text-white text-base truncate cursor-pointer hover:underline ${
route.path === pathname ? "font-bold" : "font-light"
}`}
>
{route.name}
</div>
)}
</Link>
))}
</div>
Expand Down
26 changes: 26 additions & 0 deletions client/components/SettingsLayout/LeftBarDrawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"use client";
import { useAppStore } from "@/store";
import React from "react";
import Drawer from "react-modern-drawer";
import SettingsLeftBar from "./LeftBar";
import "react-modern-drawer/dist/index.css";

const LeftBarDrawer = () => {
const isSideBarOpen = useAppStore((state) => state.isSideBarOpen);
const setIsSidebarOpen = useAppStore((state) => state.setIsSidebarOpen);
return (
<>
<Drawer
open={isSideBarOpen}
onClose={() => setIsSidebarOpen(false)}
direction="left"
className=""
zIndex={1000}
>
<SettingsLeftBar isMobileView={true} />
</Drawer>
</>
);
};

export default LeftBarDrawer;
29 changes: 29 additions & 0 deletions client/components/ui/switch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client"

import * as React from "react"
import * as SwitchPrimitives from "@radix-ui/react-switch"

import { cn } from "@/lib/utils"

const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitives.Root>
))
Switch.displayName = SwitchPrimitives.Root.displayName

export { Switch }
Loading

0 comments on commit 3c9cfb5

Please sign in to comment.