@@ -7,10 +7,16 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { ProjectCard } from "@/components/projects/ProjectCard";
|
import { ProjectCard } from "@/components/projects/ProjectCard";
|
||||||
import { NewProjectDialog } from "@/components/projects/NewProjectDialog";
|
import { NewProjectDialog } from "@/components/projects/NewProjectDialog";
|
||||||
import { Plus, Search, Loader2 } from "lucide-react";
|
import { Plus, Search, Loader2 } from "lucide-react";
|
||||||
|
import { HIDE_ARCHIVED_KEY } from "@/components/settings/HideArchivedToggle";
|
||||||
|
|
||||||
export default function ProjectsPage() {
|
export default function ProjectsPage() {
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const [showNew, setShowNew] = useState(false);
|
const [showNew, setShowNew] = useState(false);
|
||||||
|
const [hideArchived, setHideArchived] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setHideArchived(localStorage.getItem(HIDE_ARCHIVED_KEY) === "1");
|
||||||
|
}, []);
|
||||||
|
|
||||||
const { data, isLoading } = useQuery({
|
const { data, isLoading } = useQuery({
|
||||||
queryKey: ["projects", search],
|
queryKey: ["projects", search],
|
||||||
@@ -35,6 +41,8 @@ export default function ProjectsPage() {
|
|||||||
const projects = data?.projects ?? [];
|
const projects = data?.projects ?? [];
|
||||||
const clients = clientsData?.clients ?? [];
|
const clients = clientsData?.clients ?? [];
|
||||||
|
|
||||||
|
const visibleProjects = hideArchived ? projects.filter((p: any) => p.status !== "ARCHIVED") : projects;
|
||||||
|
|
||||||
const SCROLL_KEY = 'projects-scroll';
|
const SCROLL_KEY = 'projects-scroll';
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -56,6 +64,9 @@ export default function ProjectsPage() {
|
|||||||
<h1 className="text-3xl font-bold text-white">Projects</h1>
|
<h1 className="text-3xl font-bold text-white">Projects</h1>
|
||||||
<p className="text-zinc-400 mt-1">
|
<p className="text-zinc-400 mt-1">
|
||||||
{projects.length} project{projects.length !== 1 ? "s" : ""}
|
{projects.length} project{projects.length !== 1 ? "s" : ""}
|
||||||
|
{hideArchived && projects.length !== visibleProjects.length && (
|
||||||
|
<span className="text-zinc-600 ml-1">({projects.length - visibleProjects.length} archived hidden)</span>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={() => setShowNew(true)} className="gap-2">
|
<Button onClick={() => setShowNew(true)} className="gap-2">
|
||||||
@@ -94,7 +105,7 @@ export default function ProjectsPage() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||||
{projects.map((project) => (
|
{visibleProjects.map((project: any) => (
|
||||||
<div key={project.id} onClick={saveScroll}>
|
<div key={project.id} onClick={saveScroll}>
|
||||||
<ProjectCard project={project} />
|
<ProjectCard project={project} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { getInitials } from "@/lib/utils";
|
|||||||
import { ChangePasswordForm } from "@/components/settings/ChangePasswordForm";
|
import { ChangePasswordForm } from "@/components/settings/ChangePasswordForm";
|
||||||
import { HetznerConfigForm } from "@/components/settings/HetznerConfigForm";
|
import { HetznerConfigForm } from "@/components/settings/HetznerConfigForm";
|
||||||
import { SketchTemplatesSection } from "@/components/settings/SketchTemplatesSection";
|
import { SketchTemplatesSection } from "@/components/settings/SketchTemplatesSection";
|
||||||
|
import { HideArchivedToggle } from "@/components/settings/HideArchivedToggle";
|
||||||
import { getHetznerConfig } from "@/actions/settings";
|
import { getHetznerConfig } from "@/actions/settings";
|
||||||
|
|
||||||
export const metadata = { title: "Settings" };
|
export const metadata = { title: "Settings" };
|
||||||
@@ -42,6 +43,15 @@ export default async function SettingsPage() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-base">Display Preferences</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<HideArchivedToggle />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<ChangePasswordForm mustChangePassword={session.user.mustChangePassword ?? false} />
|
<ChangePasswordForm mustChangePassword={session.user.mustChangePassword ?? false} />
|
||||||
|
|
||||||
{isAdmin && hetznerConfig && (
|
{isAdmin && hetznerConfig && (
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
export const HIDE_ARCHIVED_KEY = "app:hideArchivedProjects";
|
||||||
|
|
||||||
|
export function HideArchivedToggle() {
|
||||||
|
const [checked, setChecked] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setChecked(localStorage.getItem(HIDE_ARCHIVED_KEY) === "1");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toggle = (val: boolean) => {
|
||||||
|
setChecked(val);
|
||||||
|
localStorage.setItem(HIDE_ARCHIVED_KEY, val ? "1" : "0");
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<label className="flex items-center gap-3 cursor-pointer select-none">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={checked}
|
||||||
|
onChange={(e) => toggle(e.target.checked)}
|
||||||
|
className="cursor-pointer accent-blue-500 w-4 h-4 shrink-0"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-zinc-200">Hide archived productions</p>
|
||||||
|
<p className="text-xs text-muted-foreground mt-0.5">Archived projects won't appear on the Projects page</p>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user