feat(ai): localize AI features

This commit is contained in:
Sepehr Ramezani
2026-02-15 17:38:16 +01:00
parent 8f9031f076
commit 9eb3bd912a
72 changed files with 17098 additions and 7759 deletions

View File

@@ -6,17 +6,18 @@ import { deleteUser, updateUserRole } from '@/app/actions/admin'
import { toast } from 'sonner'
import { Trash2, Shield, ShieldOff } from 'lucide-react'
import { format } from 'date-fns'
import { useLanguage } from '@/lib/i18n'
export function UserList({ initialUsers }: { initialUsers: any[] }) {
// Optimistic update could be implemented here, but standard is fine for admin
const { t } = useLanguage()
const handleDelete = async (id: string) => {
if (!confirm('Are you sure? This action cannot be undone.')) return
if (!confirm(t('admin.users.confirmDelete'))) return
try {
await deleteUser(id)
toast.success('User deleted')
toast.success(t('admin.users.deleteSuccess'))
} catch (e) {
toast.error('Failed to delete')
toast.error(t('admin.users.deleteFailed'))
}
}
@@ -24,9 +25,9 @@ export function UserList({ initialUsers }: { initialUsers: any[] }) {
const newRole = user.role === 'ADMIN' ? 'USER' : 'ADMIN'
try {
await updateUserRole(user.id, newRole)
toast.success(`User role updated to ${newRole}`)
toast.success(t('admin.users.roleUpdateSuccess', { role: newRole }))
} catch (e) {
toast.error('Failed to update role')
toast.error(t('admin.users.roleUpdateFailed'))
}
}
@@ -35,21 +36,21 @@ export function UserList({ initialUsers }: { initialUsers: any[] }) {
<table className="w-full caption-bottom text-sm text-left">
<thead className="[&_tr]:border-b">
<tr className="border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted">
<th className="h-12 px-4 align-middle font-medium text-muted-foreground">Name</th>
<th className="h-12 px-4 align-middle font-medium text-muted-foreground">Email</th>
<th className="h-12 px-4 align-middle font-medium text-muted-foreground">Role</th>
<th className="h-12 px-4 align-middle font-medium text-muted-foreground">Created At</th>
<th className="h-12 px-4 align-middle font-medium text-muted-foreground text-right">Actions</th>
<th className="h-12 px-4 align-middle font-medium text-muted-foreground">{t('admin.users.table.name')}</th>
<th className="h-12 px-4 align-middle font-medium text-muted-foreground">{t('admin.users.table.email')}</th>
<th className="h-12 px-4 align-middle font-medium text-muted-foreground">{t('admin.users.table.role')}</th>
<th className="h-12 px-4 align-middle font-medium text-muted-foreground">{t('admin.users.table.createdAt')}</th>
<th className="h-12 px-4 align-middle font-medium text-muted-foreground text-right">{t('admin.users.table.actions')}</th>
</tr>
</thead>
<tbody className="[&_tr:last-child]:border-0">
{initialUsers.map((user) => (
<tr key={user.id} className="border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted">
<td className="p-4 align-middle font-medium">{user.name || 'N/A'}</td>
<td className="p-4 align-middle font-medium">{user.name || t('common.notAvailable')}</td>
<td className="p-4 align-middle">{user.email}</td>
<td className="p-4 align-middle">
<span className={`inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 ${user.role === 'ADMIN' ? 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80' : 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80'}`}>
{user.role}
{user.role === 'ADMIN' ? t('admin.users.roles.admin') : t('admin.users.roles.user')}
</span>
</td>
<td className="p-4 align-middle">{format(new Date(user.createdAt), 'PP')}</td>
@@ -59,7 +60,7 @@ export function UserList({ initialUsers }: { initialUsers: any[] }) {
variant="ghost"
size="sm"
onClick={() => handleRoleToggle(user)}
title={user.role === 'ADMIN' ? "Demote to User" : "Promote to Admin"}
title={user.role === 'ADMIN' ? t('admin.users.demote') : t('admin.users.promote')}
>
{user.role === 'ADMIN' ? <ShieldOff className="h-4 w-4" /> : <Shield className="h-4 w-4" />}
</Button>