mirror of
https://github.com/crocofied/CoreControl.git
synced 2025-12-18 07:56:57 +00:00
New compact view for applications
This commit is contained in:
parent
677d1c5a58
commit
fde7b3b23e
BIN
ApplicationsFormatted.tsx
Normal file
BIN
ApplicationsFormatted.tsx
Normal file
Binary file not shown.
@ -25,6 +25,8 @@ import {
|
|||||||
List,
|
List,
|
||||||
Pencil,
|
Pencil,
|
||||||
Zap,
|
Zap,
|
||||||
|
ViewIcon,
|
||||||
|
Grid3X3,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@ -76,6 +78,12 @@ import {
|
|||||||
import { StatusIndicator } from "@/components/status-indicator";
|
import { StatusIndicator } from "@/components/status-indicator";
|
||||||
import { Toaster } from "@/components/ui/sonner"
|
import { Toaster } from "@/components/ui/sonner"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
|
||||||
interface Application {
|
interface Application {
|
||||||
id: number;
|
id: number;
|
||||||
@ -129,23 +137,42 @@ export default function Dashboard() {
|
|||||||
const savedLayout = Cookies.get("layoutPreference-app");
|
const savedLayout = Cookies.get("layoutPreference-app");
|
||||||
const savedItemsPerPage = Cookies.get("itemsPerPage-app");
|
const savedItemsPerPage = Cookies.get("itemsPerPage-app");
|
||||||
const initialIsGridLayout = savedLayout === "grid";
|
const initialIsGridLayout = savedLayout === "grid";
|
||||||
const defaultItemsPerPage = initialIsGridLayout ? 15 : 5;
|
const initialIsCompactLayout = savedLayout === "compact";
|
||||||
|
const defaultItemsPerPage = initialIsGridLayout ? 15 : (initialIsCompactLayout ? 30 : 5);
|
||||||
const initialItemsPerPage = savedItemsPerPage ? parseInt(savedItemsPerPage) : defaultItemsPerPage;
|
const initialItemsPerPage = savedItemsPerPage ? parseInt(savedItemsPerPage) : defaultItemsPerPage;
|
||||||
|
|
||||||
const [isGridLayout, setIsGridLayout] = useState<boolean>(initialIsGridLayout);
|
const [isGridLayout, setIsGridLayout] = useState<boolean>(initialIsGridLayout);
|
||||||
|
const [isCompactLayout, setIsCompactLayout] = useState<boolean>(initialIsCompactLayout);
|
||||||
const [itemsPerPage, setItemsPerPage] = useState<number>(initialItemsPerPage);
|
const [itemsPerPage, setItemsPerPage] = useState<number>(initialItemsPerPage);
|
||||||
const customInputRef = useRef<HTMLInputElement>(null);
|
const customInputRef = useRef<HTMLInputElement>(null);
|
||||||
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
|
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
const toggleLayout = () => {
|
const toggleLayout = (layout: string) => {
|
||||||
const newLayout = !isGridLayout;
|
if (layout === "standard") {
|
||||||
setIsGridLayout(newLayout);
|
setIsGridLayout(false);
|
||||||
Cookies.set("layoutPreference-app", newLayout ? "grid" : "standard", {
|
setIsCompactLayout(false);
|
||||||
|
Cookies.set("layoutPreference-app", "standard", {
|
||||||
expires: 365,
|
expires: 365,
|
||||||
path: "/",
|
path: "/",
|
||||||
sameSite: "strict",
|
sameSite: "strict",
|
||||||
});
|
});
|
||||||
// Don't automatically change itemsPerPage when layout changes
|
} else if (layout === "grid") {
|
||||||
|
setIsGridLayout(true);
|
||||||
|
setIsCompactLayout(false);
|
||||||
|
Cookies.set("layoutPreference-app", "grid", {
|
||||||
|
expires: 365,
|
||||||
|
path: "/",
|
||||||
|
sameSite: "strict",
|
||||||
|
});
|
||||||
|
} else if (layout === "compact") {
|
||||||
|
setIsGridLayout(false);
|
||||||
|
setIsCompactLayout(true);
|
||||||
|
Cookies.set("layoutPreference-app", "compact", {
|
||||||
|
expires: 365,
|
||||||
|
path: "/",
|
||||||
|
sameSite: "strict",
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleItemsPerPageChange = (value: string) => {
|
const handleItemsPerPageChange = (value: string) => {
|
||||||
@ -335,20 +362,30 @@ export default function Dashboard() {
|
|||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<span className="text-3xl font-bold">Your Applications</span>
|
<span className="text-3xl font-bold">Your Applications</span>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button
|
<DropdownMenu>
|
||||||
variant="outline"
|
<DropdownMenuTrigger asChild>
|
||||||
size="icon"
|
<Button variant="outline" size="icon" title="Change view">
|
||||||
onClick={toggleLayout}
|
{isCompactLayout ? (
|
||||||
title={
|
<Grid3X3 className="h-4 w-4" />
|
||||||
isGridLayout ? "Switch to list view" : "Switch to grid view"
|
) : isGridLayout ? (
|
||||||
}
|
|
||||||
>
|
|
||||||
{isGridLayout ? (
|
|
||||||
<List className="h-4 w-4" />
|
|
||||||
) : (
|
|
||||||
<LayoutGrid className="h-4 w-4" />
|
<LayoutGrid className="h-4 w-4" />
|
||||||
|
) : (
|
||||||
|
<List className="h-4 w-4" />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
<DropdownMenuItem onClick={() => toggleLayout("standard")}>
|
||||||
|
<List className="h-4 w-4 mr-2" /> List View
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem onClick={() => toggleLayout("grid")}>
|
||||||
|
<LayoutGrid className="h-4 w-4 mr-2" /> Grid View
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem onClick={() => toggleLayout("compact")}>
|
||||||
|
<Grid3X3 className="h-4 w-4 mr-2" /> Compact View
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
<Select
|
<Select
|
||||||
value={String(itemsPerPage)}
|
value={String(itemsPerPage)}
|
||||||
onValueChange={handleItemsPerPageChange}
|
onValueChange={handleItemsPerPageChange}
|
||||||
@ -559,12 +596,40 @@ export default function Dashboard() {
|
|||||||
{!loading ? (
|
{!loading ? (
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
isGridLayout
|
isCompactLayout
|
||||||
|
? "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-2"
|
||||||
|
: isGridLayout
|
||||||
? "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
|
? "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
|
||||||
: "space-y-4"
|
: "space-y-4"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{applications.map((app) => (
|
{applications.map((app) => (
|
||||||
|
isCompactLayout ? (
|
||||||
|
<div
|
||||||
|
key={app.id}
|
||||||
|
className="bg-card rounded-md border p-3 flex flex-col items-center justify-between h-[120px] w-full cursor-pointer hover:shadow-md transition-shadow relative"
|
||||||
|
onClick={() => window.open(app.publicURL, "_blank")}
|
||||||
|
title={app.name}
|
||||||
|
>
|
||||||
|
<div className="absolute top-1 right-1">
|
||||||
|
<StatusIndicator isOnline={app.online} showLabel={false} />
|
||||||
|
</div>
|
||||||
|
<div className="w-16 h-16 flex-shrink-0 flex items-center justify-center">
|
||||||
|
{app.icon ? (
|
||||||
|
<img
|
||||||
|
src={app.icon}
|
||||||
|
alt={app.name}
|
||||||
|
className="w-full h-full object-contain rounded-md"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span className="text-gray-500 text-xs">Icon</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="text-center mt-2">
|
||||||
|
<h3 className="text-sm font-medium truncate w-full max-w-[110px]">{app.name}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<Card
|
<Card
|
||||||
key={app.id}
|
key={app.id}
|
||||||
className={
|
className={
|
||||||
@ -775,6 +840,7 @@ export default function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
</Card>
|
</Card>
|
||||||
|
)
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user