bugfix: invitation link copy option disapearing fixed. route problem for unauthorizrd users opening invitation link fixed.

This commit is contained in:
2026-05-17 00:02:13 +03:30
parent 627219df58
commit 2d43254af2
14 changed files with 561 additions and 205 deletions

View File

@@ -0,0 +1,146 @@
'use client';
import { Check, Copy } from 'lucide-react';
import type { OrganizationInvitationHistoryItemDto } from '@/lib/api/organization';
import { canShareOrganizationInviteLink } from '@/components/invitations/organizationInviteLinks';
import { Badge, organizationLinkStatusVariant } from '@/components/ui/common/Badge';
import { Button } from '@/components/ui/common/Button';
import { Table } from '@/components/ui/common/Table';
function formatInvitationStatusLabel(status: OrganizationInvitationHistoryItemDto['status']): string {
if (status === 'PENDING') return 'Invitation pending';
if (status === 'ACTIVE') return 'Invitation Accepted';
if (status === 'REJECTED') return 'Invitation rejected';
if (status === 'EXPIRED') return 'Invitation expired';
return status;
}
function formatTableDate(value: string): string {
const d = new Date(value);
if (Number.isNaN(d.getTime())) return '—';
return d.toLocaleDateString();
}
type InvitationHistoryDialogProps = {
open: boolean;
onClose: () => void;
loading: boolean;
items: OrganizationInvitationHistoryItemDto[];
copiedId: string | null;
copyingInvitationId: string | null;
onCopy: (invitation: OrganizationInvitationHistoryItemDto) => void;
copyError?: string;
copySuccess?: string;
};
export function InvitationHistoryDialog({
open,
onClose,
loading,
items,
copiedId,
copyingInvitationId,
onCopy,
copyError,
copySuccess,
}: InvitationHistoryDialogProps) {
if (!open) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50">
<div
className="w-full max-w-3xl max-h-[90vh] overflow-y-auto rounded-[var(--radius-md)] border border-border bg-background-secondary p-6 shadow-xl space-y-4"
role="dialog"
aria-modal="true"
aria-labelledby="invitation-history-title"
>
<div className="flex items-center justify-between gap-3">
<h2 id="invitation-history-title" className="text-lg font-semibold text-text-primary">
Invitation History
</h2>
<Button type="button" size="sm" onClick={onClose}>
Close
</Button>
</div>
{copyError && (
<div className="rounded-[var(--radius-md)] border border-red-500/40 bg-red-500/10 px-4 py-3 text-sm text-red-700 dark:text-red-300">
{copyError}
</div>
)}
{copySuccess && (
<div className="rounded-[var(--radius-md)] border border-primary/30 bg-primary-soft/40 px-4 py-3 text-sm text-text-primary">
{copySuccess}
</div>
)}
{loading ? (
<p className="text-sm text-text-secondary">Loading invitation history...</p>
) : items.length === 0 ? (
<p className="text-sm text-text-secondary">No invitations yet.</p>
) : (
<Table
headers={
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
Organization
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
Owner email
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
Date
</th>
<th className="px-6 py-3 text-center text-xs font-medium text-text-muted uppercase tracking-wider">
Status
</th>
<th className="px-6 py-3 text-right text-xs font-medium text-text-muted uppercase tracking-wider">
Action
</th>
</tr>
}
body={
<>
{items.map((inv) => (
<tr key={inv.id} className="hover:bg-background-secondary/45">
<td className="px-6 py-1.5 text-sm text-text-primary">{inv.organizationName}</td>
<td className="px-6 py-1.5 text-sm text-text-secondary">{inv.ownerEmail}</td>
<td className="px-6 py-1.5 text-sm text-text-secondary">
{formatTableDate(inv.createdAt)}
</td>
<td className="px-6 py-1.5 text-center align-middle">
<Badge variant={organizationLinkStatusVariant(inv.status)} fixedWidth={false}>
{formatInvitationStatusLabel(inv.status)}
</Badge>
</td>
<td className="px-6 py-1.5 text-right align-middle">
{canShareOrganizationInviteLink(inv) ? (
<button
type="button"
className="p-2 rounded-md text-text-secondary hover:bg-background-card/80 hover:text-text-primary disabled:opacity-50"
disabled={copyingInvitationId === inv.id}
onClick={() => onCopy(inv)}
aria-label="Copy invitation link"
title="Copy invitation link (generates a new link if needed)"
>
{copiedId === inv.id ? (
<Check className="w-4 h-4" />
) : (
<Copy className="w-4 h-4" />
)}
</button>
) : (
<span className="text-xs text-text-muted"></span>
)}
</td>
</tr>
))}
</>
}
/>
)}
</div>
</div>
);
}