Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds ability for admins to edit navigation toggles #154

Merged
merged 4 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion apps/web/src/actions/admin/modify-nav-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import { revalidatePath } from "next/cache";

const metadataSchema = z.object({
name: z.string().min(1),
url: z.string(),
url: z.string().min(1),
});

const editMetadataSchema = metadataSchema.extend({
existingName: z.string().min(1),
enabled: z.boolean(),
});

// Maybe a better way to do this for revalidation? Who knows.
Expand All @@ -26,6 +31,28 @@ export const setItem = adminAction
return { success: true };
});

export const editItem = adminAction
.schema(editMetadataSchema)
.action(async ({ parsedInput: { name, url, existingName } }) => {
const pipe = kv.pipeline();

if (existingName != name) {
pipe.srem("config:navitemslist", encodeURIComponent(existingName));
}

pipe.sadd("config:navitemslist", encodeURIComponent(name));
pipe.hset(`config:navitems:${encodeURIComponent(name)}`, {
url,
name,
enabled: true,
});

await pipe.exec();

revalidatePath(navAdminPage);
return { success: true };
});

export const removeItem = adminAction
.schema(z.string())
.action(async ({ parsedInput: name, ctx: { user, userId } }) => {
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/app/admin/toggles/landing/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
NavItemsManager,
NavItemDialog,
AddNavItemDialog,
} from "@/components/admin/toggles/NavItemsManager";
import { getAllNavItems } from "@/lib/utils/server/redis";

Expand All @@ -13,7 +13,7 @@ export default async function Page() {
Navbar Items
</h2>
<div className="ml-auto">
<NavItemDialog />
<AddNavItemDialog />
</div>
</div>
<NavItemsManager
Expand Down
110 changes: 105 additions & 5 deletions apps/web/src/components/admin/toggles/NavItemsManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
setItem,
removeItem,
toggleItem,
editItem,
} from "@/actions/admin/modify-nav-item";
import { toast } from "sonner";
import Link from "next/link";
Expand All @@ -43,6 +44,9 @@ export function NavItemsManager({ navItems }: NavItemsManagerProps) {
onSuccess: () => {
toast.success("NavItem deleted successfully!");
},
onError: () => {
toast.error("Error deleting NavItem");
},
});

return (
Expand Down Expand Up @@ -79,10 +83,12 @@ export function NavItemsManager({ navItems }: NavItemsManagerProps) {
name={item.name}
/>
</TableCell>
<TableCell className="space-x-2 text-right">
<Button onClick={() => alert("Coming soon...")}>
Edit
</Button>
<TableCell className="space-x-2 space-y-2 text-right">
<EditNavItemDialog
existingName={item.name}
existingUrl={item.url}
existingEnabled={item.enabled}
/>
<Button
onClick={() => {
execute(item.name);
Expand Down Expand Up @@ -113,6 +119,9 @@ function ToggleSwitch({
updateFn: (state, { statusToSet }) => {
return { itemStatus: statusToSet };
},
onError: () => {
toast.error("Error toggling NavItem");
},
});

return (
Expand All @@ -125,7 +134,7 @@ function ToggleSwitch({
);
}

export function NavItemDialog() {
export function AddNavItemDialog() {
const [name, setName] = useState<string | null>(null);
const [url, setUrl] = useState<string | null>(null);
const [open, setOpen] = useState(false);
Expand All @@ -136,6 +145,9 @@ export function NavItemDialog() {
setOpen(false);
toast.success("NavItem created successfully!");
},
onError: () => {
toast.error("Error creating NavItem");
},
});

return (
Expand Down Expand Up @@ -183,6 +195,7 @@ export function NavItemDialog() {
console.log("Running Action");
if (!name || !url)
return alert("Please fill out all fields.");

execute({ name, url });
}}
>
Expand All @@ -193,3 +206,90 @@ export function NavItemDialog() {
</Dialog>
);
}

interface EditNavItemDialogProps {
existingName: string;
existingUrl: string;
existingEnabled: boolean;
}

function EditNavItemDialog({
existingName,
existingUrl,
existingEnabled,
}: EditNavItemDialogProps) {
const [name, setName] = useState<string>(existingName);
const [url, setUrl] = useState<string>(existingUrl);
const [open, setOpen] = useState(false);

const { execute } = useAction(editItem, {
onSuccess: () => {
console.log("Success");
setOpen(false);
toast.success("NavItem edited successfully!");
},
onError: () => {
toast.error("Error editing NavItem");
},
});

return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button>Edit Item</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Edit Item</DialogTitle>
<DialogDescription>
Edit an existing item shown in the non-dashboard navbar
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-right">
Name
</Label>
<Input
onChange={(e) => setName(e.target.value)}
id="name"
placeholder="A Cool Hyperlink"
className="col-span-3"
value={name}
/>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-right">
URL
</Label>
<Input
onChange={(e) => setUrl(e.target.value)}
id="name"
placeholder="https://example.com/"
className="col-span-3"
value={url}
/>
</div>
</div>
<DialogFooter>
<Button
onClick={() => {
console.log("Running Action");
if (!name || !url)
return alert("Please fill out all fields.");

execute({
enabled: existingEnabled,
existingName,
name,
url,
});
}}
>
Edit
christianhelp marked this conversation as resolved.
Show resolved Hide resolved
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
Loading