improvement: rtl direction, solar calendar and persian formatting added for persian users.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useLocale, useTranslations } from 'next-intl';
|
||||
import { DialogCloseButton } from '@/components/ui/shared/DialogCloseButton';
|
||||
import { Card } from '@/components/ui/shared/Card';
|
||||
import {
|
||||
@@ -12,12 +12,7 @@ import { CopyInvitationLinkButton } from '@/components/ui/organizations/CopyInvi
|
||||
import type { OrganizationInvitationHistoryItemDto } from '@/lib/api/organization';
|
||||
import { Badge } from '@/components/ui/shared/Badge';
|
||||
import { organizationConnectionStatusVariant } from '@/components/organizations/connectionStatusVariant';
|
||||
|
||||
function formatTableDate(value: string): string {
|
||||
const d = new Date(value);
|
||||
if (Number.isNaN(d.getTime())) return '—';
|
||||
return d.toLocaleDateString();
|
||||
}
|
||||
import { formatAppTableDate } from '@/lib/i18n/format';
|
||||
|
||||
type InvitationHistoryDialogProps = {
|
||||
open: boolean;
|
||||
@@ -38,6 +33,7 @@ export function InvitationHistoryDialog({
|
||||
copyingInvitationId,
|
||||
onCopy,
|
||||
}: InvitationHistoryDialogProps) {
|
||||
const locale = useLocale();
|
||||
const t = useTranslations('organizations');
|
||||
|
||||
function formatInvitationStatusLabel(
|
||||
@@ -82,7 +78,7 @@ export function InvitationHistoryDialog({
|
||||
<div className="min-w-0">
|
||||
<p className="font-medium text-text-primary truncate">{inv.organizationName}</p>
|
||||
<p className="text-sm text-text-secondary truncate mt-0.5">{inv.ownerEmail}</p>
|
||||
<p className="text-xs text-text-muted mt-1">{formatTableDate(inv.createdAt)}</p>
|
||||
<p className="text-xs text-text-muted mt-1">{formatAppTableDate(inv.createdAt, locale)}</p>
|
||||
</div>
|
||||
<Badge variant={organizationConnectionStatusVariant(inv.status)} fixedWidth={false}>
|
||||
{formatInvitationStatusLabel(inv.status)}
|
||||
@@ -105,19 +101,19 @@ export function InvitationHistoryDialog({
|
||||
<Table
|
||||
headers={
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
<th className="text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
{t('tableOrganization')}
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
<th className="text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
{t('tableOwnerEmail')}
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
<th className="text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
{t('tableDate')}
|
||||
</th>
|
||||
<th className="px-6 py-3 text-center text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
<th className="text-center text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
{t('tableStatus')}
|
||||
</th>
|
||||
<th className="px-6 py-3 text-right text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
<th className="text-end text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
{t('tableInvitationLink')}
|
||||
</th>
|
||||
</tr>
|
||||
@@ -126,17 +122,17 @@ export function InvitationHistoryDialog({
|
||||
<>
|
||||
{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 className="text-sm text-text-primary">{inv.organizationName}</td>
|
||||
<td className="text-sm text-text-secondary">{inv.ownerEmail}</td>
|
||||
<td className="text-sm text-text-secondary">
|
||||
{formatAppTableDate(inv.createdAt, locale)}
|
||||
</td>
|
||||
<td className="px-6 py-1.5 text-center align-middle">
|
||||
<td className="text-center align-middle">
|
||||
<Badge variant={organizationConnectionStatusVariant(inv.status)} fixedWidth={false}>
|
||||
{formatInvitationStatusLabel(inv.status)}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-6 py-1.5 text-right align-middle">
|
||||
<td className="text-end align-middle">
|
||||
<CopyInvitationLinkButton
|
||||
invitation={inv}
|
||||
copied={copiedId === inv.id}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useLocale, useTranslations } from 'next-intl';
|
||||
import { useToast } from '@/lib/hooks/useToast';
|
||||
import { Check, Trash2, UserPlus, X } from 'lucide-react';
|
||||
import { useAuth } from '@/lib/hooks/useAuth';
|
||||
@@ -26,6 +26,7 @@ import { SearchBar } from '@/components/ui/shared/SearchBar';
|
||||
import { Table } from '@/components/ui/shared/Table';
|
||||
import { getUserFacingError } from '@/components/shared/formatApiError';
|
||||
import { useRouter } from '@/i18n/navigation';
|
||||
import { formatAppTableDate } from '@/lib/i18n/format';
|
||||
|
||||
function formatOrganizationStatusLabel(status: string): string {
|
||||
if (!status) return status;
|
||||
@@ -33,15 +34,10 @@ function formatOrganizationStatusLabel(status: string): string {
|
||||
return lower.charAt(0).toUpperCase() + lower.slice(1);
|
||||
}
|
||||
|
||||
function formatTableDate(value: string): string {
|
||||
const d = new Date(value);
|
||||
if (Number.isNaN(d.getTime())) return '\u2014';
|
||||
return d.toLocaleDateString();
|
||||
}
|
||||
|
||||
type TableMode = 'existing' | 'search';
|
||||
|
||||
export function OrganizationsPage() {
|
||||
const locale = useLocale();
|
||||
const t = useTranslations('organizations');
|
||||
const tErrors = useTranslations('errors');
|
||||
const tNav = useTranslations('nav');
|
||||
@@ -58,6 +54,11 @@ export function OrganizationsPage() {
|
||||
[tCommon, tErrors],
|
||||
);
|
||||
|
||||
const formatTableDate = useCallback(
|
||||
(value: string) => formatAppTableDate(value, locale),
|
||||
[locale],
|
||||
);
|
||||
|
||||
const formatConnectionStatusLabel = useCallback(
|
||||
(row: CounterpartItemDto, currentOrganizationId: string): string => {
|
||||
if (row.status === 'PENDING') {
|
||||
@@ -389,19 +390,19 @@ export function OrganizationsPage() {
|
||||
<Table
|
||||
headers={
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
<th className="text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
{t('tableOrganization')}
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
<th className="text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
{t('tableOwnerEmail')}
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
<th className="text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
{t('tableDate')}
|
||||
</th>
|
||||
<th className="px-6 py-3 text-center text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
<th className="text-center text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
{t('tableStatus')}
|
||||
</th>
|
||||
<th className="px-6 py-3 text-right text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
<th className="text-end text-xs font-medium text-text-muted uppercase tracking-wider">
|
||||
{t('tableAction')}
|
||||
</th>
|
||||
</tr>
|
||||
@@ -410,14 +411,14 @@ export function OrganizationsPage() {
|
||||
<>
|
||||
{loading || (mode === 'search' && searching) ? (
|
||||
<tr>
|
||||
<td colSpan={5} className="px-6 py-8 text-sm text-text-secondary">
|
||||
<td colSpan={5} className="py-8 text-sm text-text-secondary">
|
||||
{tCommon('loadingEllipsis')}
|
||||
</td>
|
||||
</tr>
|
||||
) : mode === 'existing' ? (
|
||||
existingRows.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={5} className="px-6 py-8 text-sm text-text-secondary">
|
||||
<td colSpan={5} className="py-8 text-sm text-text-secondary">
|
||||
{t('emptyConnections')}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -434,19 +435,19 @@ export function OrganizationsPage() {
|
||||
|
||||
return (
|
||||
<tr key={row.id} className="hover:bg-background-secondary/45">
|
||||
<td className="px-6 py-1.5 text-sm font-medium text-text-primary">
|
||||
<td className="text-sm font-medium text-text-primary">
|
||||
{row.organizationName}
|
||||
</td>
|
||||
<td className="px-6 py-1.5 text-sm text-text-secondary">{row.ownerEmail}</td>
|
||||
<td className="px-6 py-1.5 text-sm text-text-secondary">
|
||||
<td className="text-sm text-text-secondary">{row.ownerEmail}</td>
|
||||
<td className="text-sm text-text-secondary">
|
||||
{formatTableDate(row.createdAt)}
|
||||
</td>
|
||||
<td className="px-6 py-1.5 text-center align-middle">
|
||||
<td className="text-center align-middle">
|
||||
<Badge variant={organizationConnectionStatusVariant(row.status)} fixedWidth={false}>
|
||||
{formatConnectionStatusLabel(row, currentOrganization.id)}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-6 py-1.5 text-right">
|
||||
<td className="text-end">
|
||||
<div className="inline-flex items-center gap-2">
|
||||
{invitationTarget && (
|
||||
<CopyInvitationLinkButton
|
||||
@@ -503,13 +504,13 @@ export function OrganizationsPage() {
|
||||
) : searchResults.length > 0 ? (
|
||||
searchResults.map((r) => (
|
||||
<tr key={r.id} className="hover:bg-background-secondary/45">
|
||||
<td className="px-6 py-1.5 text-sm font-medium text-text-primary">{r.name}</td>
|
||||
<td className="px-6 py-1.5 text-sm text-text-secondary">{r.owner.email}</td>
|
||||
<td className="px-6 py-1.5 text-sm text-text-secondary">{t('statusToday')}</td>
|
||||
<td className="px-6 py-1.5 text-center align-middle">
|
||||
<td className="text-sm font-medium text-text-primary">{r.name}</td>
|
||||
<td className="text-sm text-text-secondary">{r.owner.email}</td>
|
||||
<td className="text-sm text-text-secondary">{t('statusToday')}</td>
|
||||
<td className="text-center align-middle">
|
||||
<Badge variant="default" fixedWidth={false}>{t('statusFound')}</Badge>
|
||||
</td>
|
||||
<td className="px-6 py-1.5 text-right">
|
||||
<td className="text-end">
|
||||
<button
|
||||
type="button"
|
||||
className="p-2 rounded-md text-text-secondary hover:bg-background-card/80 hover:text-text-primary disabled:text-text-muted disabled:opacity-50"
|
||||
@@ -527,7 +528,7 @@ export function OrganizationsPage() {
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={5} className="px-6 py-6">
|
||||
<td colSpan={5} className="py-6">
|
||||
<div className="flex flex-col gap-3">
|
||||
<p className="text-sm text-text-secondary">
|
||||
{t('noDirectoryResults')}
|
||||
|
||||
Reference in New Issue
Block a user