style(ui): replace dark hover bg from neutral-950 to neutral-900 and use RelativeDate component in UsersPage
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Card, Table, Badge, StatusBadge, Button, UserAvatar } from '@zen/core/shared/components';
|
||||
import { Card, Table, Badge, StatusBadge, Button, UserAvatar, RelativeDate } from '@zen/core/shared/components';
|
||||
import { PencilEdit01Icon } from '@zen/core/shared/icons';
|
||||
import { useToast } from '@zen/core/toast';
|
||||
import AdminHeader from '../components/AdminHeader.js';
|
||||
@@ -65,11 +65,7 @@ const UsersPageClient = () => {
|
||||
key: 'created_at',
|
||||
label: 'Créé le',
|
||||
sortable: true,
|
||||
render: (user) => (
|
||||
<span className="text-sm text-neutral-600 dark:text-gray-300">
|
||||
{formatDate(user.created_at)}
|
||||
</span>
|
||||
),
|
||||
render: (user) => <RelativeDate date={user.created_at} />,
|
||||
skeleton: { height: 'h-4', width: '70%' },
|
||||
},
|
||||
{
|
||||
@@ -144,21 +140,6 @@ const UsersPageClient = () => {
|
||||
setSortOrder(newSortOrder);
|
||||
};
|
||||
|
||||
const formatDate = (dateString) => {
|
||||
if (!dateString) return 'N/A';
|
||||
try {
|
||||
return new Date(dateString).toLocaleDateString('fr-FR', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
} catch {
|
||||
return 'Date invalide';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card variant="default" padding="none">
|
||||
|
||||
@@ -25,7 +25,7 @@ const Breadcrumb = ({ items = [], className = '' }) => {
|
||||
item.active
|
||||
? 'text-black dark:text-white cursor-default'
|
||||
: item.onClick
|
||||
? 'text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-300 hover:bg-neutral-50 dark:hover:bg-neutral-950 cursor-pointer'
|
||||
? 'text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-300 hover:bg-neutral-50 dark:hover:bg-neutral-900 cursor-pointer'
|
||||
: 'text-neutral-400 cursor-default',
|
||||
].filter(Boolean).join(' ')}
|
||||
>
|
||||
|
||||
@@ -14,7 +14,7 @@ const FilterTabs = ({ tabs = [], value, onChange, className = ''}) => {
|
||||
index !== 0 ? 'border-l border-neutral-200 dark:border-[#1B1B1B]' : '',
|
||||
value === tab.key
|
||||
? 'bg-neutral-100 dark:bg-neutral-700/40 text-neutral-900 dark:text-white'
|
||||
: 'bg-white dark:bg-[#0B0B0B] text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-300 hover:bg-neutral-50 dark:hover:bg-neutral-950',
|
||||
: 'bg-white dark:bg-[#0B0B0B] text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-300 hover:bg-neutral-50 dark:hover:bg-neutral-900',
|
||||
].filter(Boolean).join(' ')}
|
||||
>
|
||||
{tab.label}
|
||||
|
||||
@@ -67,7 +67,7 @@ export const TableSkeleton = ({ rows = 5, columns = 4 }) => (
|
||||
</thead>
|
||||
<tbody className="divide-y divide-neutral-200 dark:divide-neutral-700/30">
|
||||
{Array.from({ length: rows }).map((_, rowIndex) => (
|
||||
<tr key={rowIndex} className="hover:bg-neutral-50 dark:hover:bg-neutral-950 transition-colors">
|
||||
<tr key={rowIndex} className="hover:bg-neutral-50 dark:hover:bg-neutral-900 transition-colors">
|
||||
{Array.from({ length: columns }).map((_, colIndex) => (
|
||||
<td key={colIndex} className="px-6 py-4 whitespace-nowrap">
|
||||
<Skeleton height="h-4" width={`${60 + Math.random() * 30}%`} />
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
'use client';
|
||||
|
||||
const DEFAULT_CLASS = 'text-xs text-neutral-400 dark:text-gray-500 font-ibm-plex-mono';
|
||||
|
||||
function getRelativeTime(date) {
|
||||
if (!date) return null;
|
||||
const d = date instanceof Date ? date : new Date(date);
|
||||
if (isNaN(d.getTime())) return null;
|
||||
|
||||
const rtf = new Intl.RelativeTimeFormat('fr', { numeric: 'auto' });
|
||||
const diffMs = d.getTime() - Date.now();
|
||||
const diffSec = Math.round(diffMs / 1000);
|
||||
const absMin = Math.abs(Math.round(diffSec / 60));
|
||||
const absH = Math.abs(Math.round(diffSec / 3600));
|
||||
const absDays = Math.abs(Math.round(diffSec / 86400));
|
||||
|
||||
if (Math.abs(diffSec) < 60) return rtf.format(diffSec, 'second');
|
||||
if (absMin < 60) return rtf.format(Math.round(diffSec / 60), 'minute');
|
||||
if (absH < 24) return rtf.format(Math.round(diffSec / 3600), 'hour');
|
||||
if (absDays < 7) return rtf.format(Math.round(diffSec / 86400), 'day');
|
||||
if (absDays < 30) return rtf.format(Math.round(diffSec / 604800), 'week');
|
||||
if (absDays < 365) return rtf.format(Math.round(diffSec / 2592000), 'month');
|
||||
return rtf.format(Math.round(diffSec / 31536000), 'year');
|
||||
}
|
||||
|
||||
const RelativeDate = ({ date, className = DEFAULT_CLASS, ...props }) => {
|
||||
const text = getRelativeTime(date);
|
||||
if (!text) return null;
|
||||
|
||||
const d = date instanceof Date ? date : new Date(date);
|
||||
|
||||
return (
|
||||
<time dateTime={d.toISOString()} className={className} title={d.toLocaleString('fr-FR')} {...props}>
|
||||
{text}
|
||||
</time>
|
||||
);
|
||||
};
|
||||
|
||||
export default RelativeDate;
|
||||
@@ -60,7 +60,7 @@ const Table = ({
|
||||
);
|
||||
|
||||
const SkeletonRow = () => (
|
||||
<tr className="hover:bg-neutral-100 dark:hover:bg-neutral-950 transition-colors">
|
||||
<tr className="hover:bg-neutral-100 dark:hover:bg-neutral-900 transition-colors">
|
||||
{columns.map((column, index) => (
|
||||
<td key={index} className={`${sizeClasses.cell} ${column.noWrap !== false ? '' : 'whitespace-nowrap'}`}>
|
||||
{column.skeleton ? (
|
||||
|
||||
@@ -28,3 +28,4 @@ export { default as Switch } from './Switch';
|
||||
export { default as TagInput } from './TagInput';
|
||||
export { default as UserAvatar } from './UserAvatar';
|
||||
export { default as ColorPicker } from './ColorPicker.client';
|
||||
export { default as RelativeDate } from './RelativeDate.client';
|
||||
|
||||
Reference in New Issue
Block a user