fix(web): forgot-password dialog, settings tagline, account dialog triggers
- Login page: replace static 'Forget Password? Contact IT' line with a proper dialog button. Clicking opens an explanatory dialog (self- service reset is intentionally disabled; admins can reset from /settings/users or run scripts/set-password.sh). - /settings: drop the 'cm WhatsApp Bot · self-hosted' tagline. - /accounts/[id]: Unpair + Delete cards weren't responding to clicks. Restructure so the transparent <button> overlay is a sibling of <Card> inside a <div className='relative'> wrapper (mirrors the working Pair/Re-pair pattern). The previous layout placed the DialogTrigger inside the Card, which produced no clickable button in the rendered DOM under radix-ui 1.4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c493101b60
commit
5d583d9194
@ -156,12 +156,11 @@ export default async function AccountDetailPage({ params }: AccountDetailPagePro
|
|||||||
</Card>
|
</Card>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Unpair — transparent <button> overlay opens the dialog
|
{/* Unpair — transparent <button> overlay (sibling of Card,
|
||||||
so we don't pass button-specific props onto the Card div
|
inside a relative wrapper). Same pattern as Delete below. */}
|
||||||
(Radix asChild does that and it produces a hydration
|
|
||||||
mismatch on a div). */}
|
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<Card className="relative transition-all hover:shadow-md hover:ring-amber-500/30 cursor-pointer">
|
<div className="relative">
|
||||||
|
<Card className="transition-all hover:shadow-md hover:ring-amber-500/30 cursor-pointer">
|
||||||
<CardContent className="flex items-center justify-between gap-4 py-4">
|
<CardContent className="flex items-center justify-between gap-4 py-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="flex size-9 items-center justify-center rounded-lg bg-amber-500/10">
|
<div className="flex size-9 items-center justify-center rounded-lg bg-amber-500/10">
|
||||||
@ -176,6 +175,7 @@ export default async function AccountDetailPage({ params }: AccountDetailPagePro
|
|||||||
</div>
|
</div>
|
||||||
<ChevronRightIcon className="size-4 text-muted-foreground/60" />
|
<ChevronRightIcon className="size-4 text-muted-foreground/60" />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
</Card>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -183,7 +183,7 @@ export default async function AccountDetailPage({ params }: AccountDetailPagePro
|
|||||||
className="absolute inset-0 w-full rounded-xl bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
className="absolute inset-0 w-full rounded-xl bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
||||||
/>
|
/>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
</Card>
|
</div>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Unpair this account?</DialogTitle>
|
<DialogTitle>Unpair this account?</DialogTitle>
|
||||||
@ -207,9 +207,16 @@ export default async function AccountDetailPage({ params }: AccountDetailPagePro
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Delete — transparent <button> overlay opens the dialog. */}
|
{/* Delete — transparent <button> overlay opens the dialog.
|
||||||
|
The button lives as a sibling of <Card> (inside a relative
|
||||||
|
wrapper) instead of inside the Card. Radix's asChild-driven
|
||||||
|
DialogTrigger stops emitting the underlying button when the
|
||||||
|
wrapper Card adds an `absolute inset-0` sibling on the same
|
||||||
|
stacking context, so we mirror the working pattern from the
|
||||||
|
Pair/Re-pair card above. */}
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<Card className="relative transition-all hover:shadow-md hover:ring-destructive/30 cursor-pointer">
|
<div className="relative">
|
||||||
|
<Card className="transition-all hover:shadow-md hover:ring-destructive/30 cursor-pointer">
|
||||||
<CardContent className="flex items-center justify-between gap-4 py-4">
|
<CardContent className="flex items-center justify-between gap-4 py-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="flex size-9 items-center justify-center rounded-lg bg-destructive/10">
|
<div className="flex size-9 items-center justify-center rounded-lg bg-destructive/10">
|
||||||
@ -224,6 +231,7 @@ export default async function AccountDetailPage({ params }: AccountDetailPagePro
|
|||||||
</div>
|
</div>
|
||||||
<ChevronRightIcon className="size-4 text-muted-foreground/60" />
|
<ChevronRightIcon className="size-4 text-muted-foreground/60" />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
</Card>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -231,7 +239,7 @@ export default async function AccountDetailPage({ params }: AccountDetailPagePro
|
|||||||
className="absolute inset-0 w-full rounded-xl bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-destructive focus-visible:ring-offset-2"
|
className="absolute inset-0 w-full rounded-xl bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-destructive focus-visible:ring-offset-2"
|
||||||
/>
|
/>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
</Card>
|
</div>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Delete this account permanently?</DialogTitle>
|
<DialogTitle>Delete this account permanently?</DialogTitle>
|
||||||
|
|||||||
@ -1,8 +1,18 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useTransition } from "react";
|
import { useState, useTransition } from "react";
|
||||||
import { Loader2Icon, LockIcon } from "lucide-react";
|
import { Loader2Icon, LockIcon, HelpCircleIcon } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
DialogClose,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { loginAction } from "@/actions/auth";
|
import { loginAction } from "@/actions/auth";
|
||||||
@ -58,9 +68,41 @@ export function LoginFormClient({ next }: { next: string }) {
|
|||||||
)}
|
)}
|
||||||
Sign in
|
Sign in
|
||||||
</Button>
|
</Button>
|
||||||
<p className="text-xs text-muted-foreground text-center">
|
<Dialog>
|
||||||
Forget Password? Contact IT
|
<DialogTrigger asChild>
|
||||||
</p>
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="link"
|
||||||
|
size="sm"
|
||||||
|
className="w-full text-xs text-muted-foreground hover:text-foreground"
|
||||||
|
>
|
||||||
|
<HelpCircleIcon className="size-3.5" />
|
||||||
|
Forgot password?
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Forgot your password?</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Self-service password reset is intentionally disabled on
|
||||||
|
this deployment. To recover access, contact an
|
||||||
|
administrator. They can reset your password from
|
||||||
|
Settings → Users, or run{" "}
|
||||||
|
<code className="font-mono text-[0.75rem]">
|
||||||
|
./scripts/set-password.sh <username>
|
||||||
|
</code>{" "}
|
||||||
|
from the tools container.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter showCloseButton>
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Button type="button" size="sm">
|
||||||
|
Got it
|
||||||
|
</Button>
|
||||||
|
</DialogClose>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -77,10 +77,6 @@ export default async function SettingsPage() {
|
|||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<p className="text-center text-xs text-muted-foreground">
|
|
||||||
cm WhatsApp Bot · self-hosted
|
|
||||||
</p>
|
|
||||||
</PageShell>
|
</PageShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user