fix: ui fixes

This commit is contained in:
gosangam
2026-06-16 16:25:54 +05:30
parent 5342123fba
commit d3c043f6b0
4 changed files with 79 additions and 6 deletions
+5 -5
View File
@@ -173,10 +173,10 @@ export default function FeedbackPage() {
}
function StatusBadge({ f }: { f: AdminFeedback }) {
const label = f.replied_at
? "Replied"
: f.resolved_at
? "Resolved"
const label = f.resolved_at
? "Resolved"
: f.replied_at
? "Replied"
: f.acknowledged_at
? "Acknowledged"
: "Open";
@@ -240,7 +240,7 @@ function ReplyModal({
onChange={(e) => setMessage(e.target.value)}
rows={5}
maxLength={4000}
placeholder="Write your reply… (emailed to the submitter, marks this resolved)"
placeholder="Write your reply… (emailed to the submitter; use Resolve separately to close it)"
/>
</label>
<div className="flex justify-end gap-2">
+39 -1
View File
@@ -1,8 +1,10 @@
"use client";
import { useCallback, useState } from "react";
import { Mail } from "lucide-react";
import { toast } from "sonner";
import { api, AdminError, type AdminUser } from "@/lib/api";
import { GoogleIcon, FacebookIcon } from "@/components/ui/provider-icons";
import { usePagedList } from "@/lib/use-paged-list";
import { ListShell, EmptyRow } from "@/components/list-shell";
import { Table, THead, TR, TH, TD } from "@/components/ui/table";
@@ -74,6 +76,7 @@ export default function UsersPage() {
<TR>
<TH>User</TH>
<TH>Email</TH>
<TH>Auth</TH>
<TH>Posts</TH>
<TH>Joined</TH>
<TH className="text-right">Actions</TH>
@@ -81,7 +84,7 @@ export default function UsersPage() {
</THead>
<tbody>
{list.items.length === 0 && !list.loading ? (
<EmptyRow cols={5} label="No users found." />
<EmptyRow cols={6} label="No users found." />
) : (
list.items.map((u) => (
<TR key={u.id}>
@@ -106,6 +109,9 @@ export default function UsersPage() {
<TD>
{u.email ?? <span className="text-muted-foreground">—</span>}
</TD>
<TD>
<AuthIcons user={u} />
</TD>
<TD>{u.post_count}</TD>
<TD className="whitespace-nowrap">{fmtDate(u.created_at)}</TD>
<TD className="text-right">
@@ -122,6 +128,38 @@ export default function UsersPage() {
);
}
function AuthIcons({ user }: { user: AdminUser }) {
const hasGoogle = user.providers.includes("google");
const hasFacebook = user.providers.includes("facebook");
if (!user.has_password && !hasGoogle && !hasFacebook) {
return <span className="text-muted-foreground">—</span>;
}
return (
<div className="flex items-center gap-2">
{user.has_password ? (
// Tooltip shows the email on hover; clicking copies it to the clipboard.
<button
type="button"
title={user.email ? `Click to copy ${user.email}` : "Email & password"}
onClick={() => {
if (!user.email) return;
// No await before the write — keeps the click's user activation.
navigator.clipboard.writeText(user.email).then(
() => toast.success("Email copied"),
() => toast.error("Could not copy"),
);
}}
className="inline-flex text-muted-foreground outline-none hover:text-foreground focus-visible:text-foreground"
>
<Mail className="size-4" />
</button>
) : null}
{hasGoogle ? <GoogleIcon className="size-4" /> : null}
{hasFacebook ? <FacebookIcon className="size-4" /> : null}
</div>
);
}
function NotifyModal({
user,
onClose,
+33
View File
@@ -0,0 +1,33 @@
// Brand glyphs for OAuth providers. lucide-react dropped brand icons, so these
// are small inline SVGs (Google's multicolour "G", Facebook's blue "f").
export function GoogleIcon({ className }: { className?: string }) {
return (
<svg viewBox="0 0 48 48" className={className} aria-hidden="true">
<path
fill="#FFC107"
d="M43.611 20.083H42V20H24v8h11.303c-1.649 4.657-6.08 8-11.303 8-6.627 0-12-5.373-12-12s5.373-12 12-12c3.059 0 5.842 1.154 7.961 3.039l5.657-5.657C34.046 6.053 29.268 4 24 4 12.955 4 4 12.955 4 24s8.955 20 20 20 20-8.955 20-20c0-1.341-.138-2.65-.389-3.917z"
/>
<path
fill="#FF3D00"
d="M6.306 14.691l6.571 4.819C14.655 15.108 18.961 12 24 12c3.059 0 5.842 1.154 7.961 3.039l5.657-5.657C34.046 6.053 29.268 4 24 4 16.318 4 9.656 8.337 6.306 14.691z"
/>
<path
fill="#4CAF50"
d="M24 44c5.166 0 9.86-1.977 13.409-5.192l-6.19-5.238C29.211 35.091 26.715 36 24 36c-5.202 0-9.619-3.317-11.283-7.946l-6.522 5.025C9.505 39.556 16.227 44 24 44z"
/>
<path
fill="#1976D2"
d="M43.611 20.083H42V20H24v8h11.303c-.792 2.237-2.231 4.166-4.087 5.571l.003-.002 6.19 5.238C36.971 39.205 44 34 44 24c0-1.341-.138-2.65-.389-3.917z"
/>
</svg>
);
}
export function FacebookIcon({ className }: { className?: string }) {
return (
<svg viewBox="0 0 24 24" className={className} aria-hidden="true" fill="#1877F2">
<path d="M24 12.073C24 5.404 18.627 0 12 0S0 5.404 0 12.073c0 6.026 4.388 11.022 10.125 11.927v-8.437H7.078v-3.49h3.047V9.41c0-3.017 1.792-4.684 4.533-4.684 1.312 0 2.686.235 2.686.235v2.97h-1.513c-1.491 0-1.956.93-1.956 1.886v2.265h3.328l-.532 3.49h-2.796v8.437C19.612 23.095 24 18.099 24 12.073z" />
</svg>
);
}
+2
View File
@@ -104,6 +104,8 @@ export type AdminUser = {
banned_at: string | null;
post_count: number;
created_at: string;
has_password: boolean;
providers: string[];
};
export type AdminPost = {