bugfix: all the toasts unfied inside organizations feature. other features still need a refactor for toasts though.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useToast } from '@/lib/hooks/useToast';
|
||||
import { Check, Trash2, UserPlus, X } from 'lucide-react';
|
||||
import { useAuth } from '@/lib/hooks/useAuth';
|
||||
import { useOrganizationInviteLinkCopy } from '@/lib/hooks/useOrganizationInviteLinkCopy';
|
||||
@@ -18,6 +19,7 @@ import { Badge, organizationConnectionStatusVariant } from '@/components/ui/comm
|
||||
import { Input } from '@/components/ui/common/Input';
|
||||
import { SearchBar } from '@/components/ui/common/SearchBar';
|
||||
import { Table } from '@/components/ui/common/Table';
|
||||
import { ToastStack } from '@/components/ui/common/Toast';
|
||||
import type { ApiError } from '@/types/api';
|
||||
|
||||
function formatOrganizationStatusLabel(status: string): string {
|
||||
@@ -63,8 +65,7 @@ type TableMode = 'existing' | 'search';
|
||||
export default function OrganizationsPage() {
|
||||
const { currentOrganization } = useAuth();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
const [success, setSuccess] = useState('');
|
||||
const toast = useToast();
|
||||
|
||||
const [query, setQuery] = useState('');
|
||||
const [mode, setMode] = useState<TableMode>('existing');
|
||||
@@ -81,8 +82,6 @@ export default function OrganizationsPage() {
|
||||
const [historyOpen, setHistoryOpen] = useState(false);
|
||||
const [historyLoading, setHistoryLoading] = useState(false);
|
||||
const [historyItems, setHistoryItems] = useState<OrganizationInvitationHistoryItemDto[]>([]);
|
||||
const [historyCopyError, setHistoryCopyError] = useState('');
|
||||
const [historyCopySuccess, setHistoryCopySuccess] = useState('');
|
||||
|
||||
const {
|
||||
copiedId,
|
||||
@@ -99,12 +98,12 @@ export default function OrganizationsPage() {
|
||||
|
||||
async function loadList() {
|
||||
setLoading(true);
|
||||
setError('');
|
||||
toast.setError('');
|
||||
try {
|
||||
const res = await organizationApi.list();
|
||||
setItems(res.data.items);
|
||||
} catch (e) {
|
||||
setError(formatApiMessage(e));
|
||||
toast.showError(formatApiMessage(e));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -114,12 +113,6 @@ export default function OrganizationsPage() {
|
||||
void loadList();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!success) return;
|
||||
const t = setTimeout(() => setSuccess(''), 4000);
|
||||
return () => clearTimeout(t);
|
||||
}, [success]);
|
||||
|
||||
async function runSearch() {
|
||||
const q = query.trim();
|
||||
if (!q) {
|
||||
@@ -130,14 +123,14 @@ export default function OrganizationsPage() {
|
||||
}
|
||||
|
||||
setSearching(true);
|
||||
setError('');
|
||||
toast.setError('');
|
||||
setMode('search');
|
||||
setShowInviteForm(false);
|
||||
try {
|
||||
const res = await organizationApi.search(q);
|
||||
setSearchResults(res.data);
|
||||
} catch (e) {
|
||||
setError(formatApiMessage(e));
|
||||
toast.showError(formatApiMessage(e));
|
||||
setSearchResults([]);
|
||||
} finally {
|
||||
setSearching(false);
|
||||
@@ -146,16 +139,16 @@ export default function OrganizationsPage() {
|
||||
|
||||
async function submitConnectionRequest(targetOrganizationId: string) {
|
||||
setPendingConnectionRowId(targetOrganizationId);
|
||||
setError('');
|
||||
toast.setError('');
|
||||
try {
|
||||
await organizationApi.createConnectionRequest(targetOrganizationId);
|
||||
setSuccess(`${counterpartLabel} connection request sent.`);
|
||||
toast.showSuccess(`${counterpartLabel} connection request sent.`);
|
||||
setSearchResults([]);
|
||||
setQuery('');
|
||||
setMode('existing');
|
||||
await loadList();
|
||||
} catch (e) {
|
||||
setError(formatApiMessage(e));
|
||||
toast.showError(formatApiMessage(e));
|
||||
} finally {
|
||||
setPendingConnectionRowId(null);
|
||||
}
|
||||
@@ -163,14 +156,14 @@ export default function OrganizationsPage() {
|
||||
|
||||
async function sendInvite() {
|
||||
setInviteLoading(true);
|
||||
setError('');
|
||||
toast.setError('');
|
||||
try {
|
||||
const res = await organizationApi.invite({
|
||||
organizationName: manualOrganizationName.trim(),
|
||||
ownerEmail: manualOwnerEmail.trim(),
|
||||
});
|
||||
storeInviteLink(res.data.invitationId, manualOwnerEmail, res.data.invitationUrl);
|
||||
setSuccess(`Invitation link created for ${manualOwnerEmail.trim()}`);
|
||||
toast.showSuccess(`Invitation link created for ${manualOwnerEmail.trim()}`);
|
||||
setManualOrganizationName('');
|
||||
setManualOwnerEmail('');
|
||||
setShowInviteForm(false);
|
||||
@@ -179,7 +172,7 @@ export default function OrganizationsPage() {
|
||||
setSearchResults([]);
|
||||
await loadList();
|
||||
} catch (e) {
|
||||
setError(formatApiMessage(e));
|
||||
toast.showError(formatApiMessage(e));
|
||||
} finally {
|
||||
setInviteLoading(false);
|
||||
}
|
||||
@@ -195,38 +188,34 @@ export default function OrganizationsPage() {
|
||||
async function openInvitationHistory() {
|
||||
setHistoryOpen(true);
|
||||
setHistoryLoading(true);
|
||||
setHistoryCopyError('');
|
||||
setHistoryCopySuccess('');
|
||||
setError('');
|
||||
toast.clear();
|
||||
try {
|
||||
await loadInvitationHistory();
|
||||
} catch (e) {
|
||||
setError(formatApiMessage(e));
|
||||
toast.showError(formatApiMessage(e));
|
||||
} finally {
|
||||
setHistoryLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleHistoryCopy(invitation: OrganizationInvitationHistoryItemDto) {
|
||||
setHistoryCopyError('');
|
||||
setHistoryCopySuccess('');
|
||||
toast.setError('');
|
||||
try {
|
||||
await copyInvitationLink(invitation, {
|
||||
onRegenerated: async () => {
|
||||
await loadInvitationHistory();
|
||||
},
|
||||
});
|
||||
setHistoryCopySuccess('Invitation link copied to clipboard.');
|
||||
setTimeout(() => setHistoryCopySuccess(''), 3000);
|
||||
toast.showSuccess('Invitation link copied to clipboard.');
|
||||
} catch (e) {
|
||||
setHistoryCopyError(formatApiMessage(e));
|
||||
toast.showError(formatApiMessage(e));
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCopyInvitationFromRow(row: CounterpartItemDto) {
|
||||
const target = invitationTargetFromConnectionRow(row, currentOrganization!.id);
|
||||
if (!target) return;
|
||||
setError('');
|
||||
toast.setError('');
|
||||
try {
|
||||
await copyInvitationLink(
|
||||
{
|
||||
@@ -243,24 +232,23 @@ export default function OrganizationsPage() {
|
||||
},
|
||||
},
|
||||
);
|
||||
setSuccess('Invitation link copied to clipboard.');
|
||||
setTimeout(() => setSuccess(''), 3000);
|
||||
toast.showSuccess('Invitation link copied to clipboard.');
|
||||
} catch (e) {
|
||||
setError(formatApiMessage(e));
|
||||
toast.showError(formatApiMessage(e));
|
||||
}
|
||||
}
|
||||
|
||||
async function respondToPendingConnection(connectionId: string, action: 'ACCEPT' | 'REJECT') {
|
||||
setPendingConnectionRowId(connectionId);
|
||||
setError('');
|
||||
toast.setError('');
|
||||
try {
|
||||
await organizationApi.respondToConnectionRequest(connectionId, action);
|
||||
setSuccess(
|
||||
toast.showSuccess(
|
||||
action === 'ACCEPT' ? 'Connection request accepted.' : 'Connection request declined.',
|
||||
);
|
||||
await loadList();
|
||||
} catch (e) {
|
||||
setError(formatApiMessage(e));
|
||||
toast.showError(formatApiMessage(e));
|
||||
} finally {
|
||||
setPendingConnectionRowId(null);
|
||||
}
|
||||
@@ -268,13 +256,13 @@ export default function OrganizationsPage() {
|
||||
|
||||
async function deleteConnection(connectionId: string) {
|
||||
setDeleteConnectionRowId(connectionId);
|
||||
setError('');
|
||||
toast.setError('');
|
||||
try {
|
||||
await organizationApi.deleteConnection(connectionId);
|
||||
setSuccess('Connection removed.');
|
||||
toast.showSuccess('Connection removed.');
|
||||
await loadList();
|
||||
} catch (e) {
|
||||
setError(formatApiMessage(e));
|
||||
toast.showError(formatApiMessage(e));
|
||||
} finally {
|
||||
setDeleteConnectionRowId(null);
|
||||
}
|
||||
@@ -306,16 +294,7 @@ export default function OrganizationsPage() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<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">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
{success && (
|
||||
<div className="rounded-[var(--radius-md)] border border-primary/30 bg-primary-soft/40 px-4 py-3 text-sm text-text-primary">
|
||||
{success}
|
||||
</div>
|
||||
)}
|
||||
{!historyOpen && <ToastStack {...toast.messages} />}
|
||||
|
||||
<SearchBar
|
||||
value={query}
|
||||
@@ -530,18 +509,13 @@ export default function OrganizationsPage() {
|
||||
|
||||
<InvitationHistoryDialog
|
||||
open={historyOpen}
|
||||
onClose={() => {
|
||||
setHistoryOpen(false);
|
||||
setHistoryCopyError('');
|
||||
setHistoryCopySuccess('');
|
||||
}}
|
||||
onClose={() => setHistoryOpen(false)}
|
||||
loading={historyLoading}
|
||||
items={historyItems}
|
||||
copiedId={copiedId}
|
||||
copyingInvitationId={copyingInvitationId}
|
||||
onCopy={(invitation) => void handleHistoryCopy(invitation)}
|
||||
copyError={historyCopyError}
|
||||
copySuccess={historyCopySuccess}
|
||||
toastMessages={toast.messages}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user