Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions frontend/src/app/(app)/admin/brevo/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { auth } from "@/auth";
import { ForbiddenPage } from "@/components/forbidden-page";
import { ROLE_IT_ADMIN } from "@/lib/roles";
import { ForbiddenPage } from "@open-elements/nextjs-app-layer";
import { ROLE_IT_ADMIN } from "@open-elements/nextjs-app-layer";
import { BrevoPageClient } from "./brevo-page-client";

export default async function BrevoPage() {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/(app)/companies/companies-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import { Plus, Trash2, Building2, Printer, Pencil, MessageSquarePlus, FileDown,
import { Button, Input, TagMultiSelect, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Skeleton } from "@open-elements/ui";
import { useTranslations } from "@/lib/i18n";
import { ActionIconButton, CopyToClipboardButton, ExternalLinkButton, TablePagination, TooltipIconButton } from "@open-elements/ui";
import { AddCommentDialog } from "@/components/add-comment-dialog";
import { AddCommentDialog } from "@open-elements/nextjs-app-layer";
import { CompanyDeleteDialog } from "@/components/company-delete-dialog";
import { CsvExportDialog } from "@/components/csv-export-dialog";
import { getCompanies, deleteCompany, getCompanyLogoUrl, createCompanyComment, getCompanyExportUrl, getTags, ForbiddenError } from "@/lib/api";
import type { CompanyDto, Page } from "@/lib/types";
import { hasRole, ROLE_ADMIN } from "@/lib/roles";
import { hasRole, ROLE_ADMIN } from "@open-elements/nextjs-app-layer";

function WebsiteCell({ value }: { readonly value: string | null }) {
if (!value) return <TableCell className="text-oe-gray-mid">—</TableCell>;
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/(app)/contacts/contacts-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import { Plus, Trash2, User, Printer, Pencil, MessageSquarePlus, FileDown, Exter
import { Button, DeleteConfirmDialog, Input, TagMultiSelect, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Skeleton } from "@open-elements/ui";
import { useTranslations } from "@/lib/i18n";
import { ActionIconButton, CopyToClipboardButton, MailtoButton, TablePagination, TooltipIconButton } from "@open-elements/ui";
import { AddCommentDialog } from "@/components/add-comment-dialog";
import { AddCommentDialog } from "@open-elements/nextjs-app-layer";
import { CsvExportDialog } from "@/components/csv-export-dialog";
import { getContacts, deleteContact, getCompaniesForSelect, getContactPhotoUrl, createContactComment, getContactExportUrl, getTags, ForbiddenError } from "@/lib/api";
import type { ContactDto, CompanyDto, Page } from "@/lib/types";
import { hasRole, ROLE_ADMIN } from "@/lib/roles";
import { hasRole, ROLE_ADMIN } from "@open-elements/nextjs-app-layer";

function EmailCell({ value }: { readonly value: string | null }) {
if (!value) return <TableCell className="text-oe-gray-mid">—</TableCell>;
Expand Down
10 changes: 4 additions & 6 deletions frontend/src/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@ import { Activity, Bell, Building2, FileText, KeyRound, RefreshCw, Settings, Tag
import { Sidebar, NavItem, CollapsibleGroup, TooltipProvider } from "@open-elements/ui";
import { useTranslations } from "@/lib/i18n";
import { getCurrentUser } from "@/lib/api";
import { hasRole, ROLE_IT_ADMIN } from "@/lib/roles";

const ADMIN_PREFIXES = ["/admin", "/api-keys", "/webhooks"];
import { hasRole, ROLE_IT_ADMIN } from "@open-elements/nextjs-app-layer";

function isAdminRoute(pathname: string): boolean {
return ADMIN_PREFIXES.some((prefix) => pathname.startsWith(prefix));
return pathname.startsWith("/admin");
}

function CrmSidebar() {
Expand Down Expand Up @@ -56,8 +54,8 @@ function CrmSidebar() {
<NavItem href="/admin/status" icon={<Activity className="h-5 w-5" />} label={t.nav.serverStatus} active={pathname.startsWith("/admin/status")} indented />
<NavItem href="/admin/token" icon={<KeyRound className="h-5 w-5" />} label={t.nav.bearerToken} active={pathname.startsWith("/admin/token")} indented />
<NavItem href="/admin/brevo" icon={<RefreshCw className="h-5 w-5" />} label={t.nav.brevo} active={pathname.startsWith("/admin/brevo")} indented />
<NavItem href="/api-keys" icon={<KeyRound className="h-5 w-5" />} label={t.nav.apiKeys} active={pathname.startsWith("/api-keys")} indented />
<NavItem href="/webhooks" icon={<Webhook className="h-5 w-5" />} label={t.nav.webhooks} active={pathname.startsWith("/webhooks")} indented />
<NavItem href="/admin/api-keys" icon={<KeyRound className="h-5 w-5" />} label={t.nav.apiKeys} active={pathname.startsWith("/admin/api-keys")} indented />
<NavItem href="/admin/webhooks" icon={<Webhook className="h-5 w-5" />} label={t.nav.webhooks} active={pathname.startsWith("/admin/webhooks")} indented />
<NavItem href="/admin/users" icon={<Users className="h-5 w-5" />} label={t.nav.users} active={pathname.startsWith("/admin/users")} indented />
<NavItem href="/admin/audit-logs" icon={<FileText className="h-5 w-5" />} label={t.nav.auditLogs} active={pathname.startsWith("/admin/audit-logs")} indented />
</CollapsibleGroup>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/(app)/tags/tags-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
import {useTranslations} from "@/lib/i18n";
import {TablePagination, TooltipIconButton} from "@open-elements/ui";
import {deleteTag, ForbiddenError, getTags} from "@/lib/api";
import {hasRole, ROLE_ADMIN} from "@/lib/roles";
import {hasRole, ROLE_ADMIN} from "@open-elements/nextjs-app-layer";
import type {Page} from "@/lib/types";

export function TagsClient() {
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/__tests__/company-detail.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ vi.mock("@/lib/api", () => ({
createCompanyComment: vi.fn(),
deleteCompanyComment: vi.fn(),
getCompanyLogoUrl: (...args: unknown[]) => mockGetCompanyLogoUrl(...args),
getTranslationSettings: () => Promise.resolve({ configured: false }),
translateText: vi.fn(),
}));

const testCompany: CompanyDto = {
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/__tests__/contact-detail.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ vi.mock("@/lib/api", () => ({
createContactComment: vi.fn(),
deleteContactComment: vi.fn(),
getContactPhotoUrl: (...args: unknown[]) => mockGetContactPhotoUrl(...args),
getTranslationSettings: () => Promise.resolve({ configured: false }),
translateText: vi.fn(),
}));

function makeContact(overrides: Partial<ContactDto> = {}): ContactDto {
Expand Down
29 changes: 0 additions & 29 deletions frontend/src/components/__tests__/forbidden-page.test.tsx

This file was deleted.

91 changes: 0 additions & 91 deletions frontend/src/components/__tests__/translate-button.test.tsx

This file was deleted.

1 change: 0 additions & 1 deletion frontend/src/components/add-comment-dialog.tsx

This file was deleted.

1 change: 0 additions & 1 deletion frontend/src/components/bearer-token-card.tsx

This file was deleted.

29 changes: 23 additions & 6 deletions frontend/src/components/company-comments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
import { useCallback, useEffect, useState } from "react";
import { useSession } from "next-auth/react";
import { MessageSquarePlus, X } from "lucide-react";
import { Button, DeleteConfirmDialog, Tooltip, TooltipTrigger, TooltipContent, Card, CardContent, CardHeader, CardTitle, Skeleton, MarkdownView } from "@open-elements/ui";
import { Button, DeleteConfirmDialog, Tooltip, TooltipTrigger, TooltipContent, Card, CardContent, CardHeader, CardTitle, Skeleton, MarkdownView, TranslateButton } from "@open-elements/ui";
import { useTranslations } from "@/lib/i18n";
import { AddCommentDialog } from "@/components/add-comment-dialog";
import { TranslateButton } from "@/components/translate-button";
import { getCompanyComments, createCompanyComment, deleteCompanyComment, ForbiddenError } from "@/lib/api";
import { AddCommentDialog } from "@open-elements/nextjs-app-layer";
import { useTranslationConfig } from "@/lib/use-translation-config";
import { getCompanyComments, createCompanyComment, deleteCompanyComment, ForbiddenError, translateText } from "@/lib/api";
import type { CommentDto } from "@/lib/types";
import { hasRole, ROLE_ADMIN } from "@/lib/roles";
import { hasRole, ROLE_ADMIN } from "@open-elements/nextjs-app-layer";

function formatDate(dateString: string, language: string): string {
const date = new Date(dateString);
Expand All @@ -29,6 +29,7 @@ interface CompanyCommentsProps {

export function CompanyComments({ companyId, totalCount }: CompanyCommentsProps) {
const t = useTranslations();
const { configured } = useTranslationConfig();
const S = t.companies.comments;
const { data: session } = useSession();
const canDelete = hasRole(session, ROLE_ADMIN);
Expand Down Expand Up @@ -132,7 +133,23 @@ export function CompanyComments({ companyId, totalCount }: CompanyCommentsProps)
{comment.author?.name ?? "—"} &middot; {formatDate(comment.createdAt, "de")}
</p>
<div className="flex items-center">
<TranslateButton text={comment.text} size="md" />
<TranslateButton
text={comment.text}
size="md"
configured={configured}
onTranslate={(text, lang) => translateText(text, lang as "de" | "en")}
translations={{
button: t.translation.translate,
dialog: {
title: t.translation.title,
loading: t.translation.loading,
error: t.translation.error,
copy: t.translation.copy,
copied: t.translation.copied,
close: t.translation.close,
},
}}
/>
<Tooltip>
<TooltipTrigger asChild>
<span>
Expand Down
27 changes: 22 additions & 5 deletions frontend/src/components/company-detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ import { useRouter } from "next/navigation";
import { useSession } from "next-auth/react";
import Link from "next/link";
import { Pencil, Trash2, Users, Building2 } from "lucide-react";
import { Button, Card, CardContent, CardHeader, CardTitle, DetailField, Separator, Tooltip, TooltipContent, TooltipTrigger, TagChips, MarkdownView } from "@open-elements/ui";
import { Button, Card, CardContent, CardHeader, CardTitle, DetailField, Separator, Tooltip, TooltipContent, TooltipTrigger, TagChips, MarkdownView, TranslateButton } from "@open-elements/ui";
import type { TagDto } from "@open-elements/ui";
import { useTranslations } from "@/lib/i18n";
import { CompanyDeleteDialog } from "@/components/company-delete-dialog";
import { CompanyComments } from "@/components/company-comments";
import { TranslateButton } from "@/components/translate-button";
import { deleteCompany, ForbiddenError, getCompanyLogoUrl, getTag } from "@/lib/api";
import { useTranslationConfig } from "@/lib/use-translation-config";
import { deleteCompany, ForbiddenError, getCompanyLogoUrl, getTag, translateText } from "@/lib/api";
import type { CompanyDto } from "@/lib/types";
import { hasRole, ROLE_ADMIN } from "@/lib/roles";
import { hasRole, ROLE_ADMIN } from "@open-elements/nextjs-app-layer";

export function CompanyDetail({ company }: { readonly company: CompanyDto }) {
const t = useTranslations();
const { configured } = useTranslationConfig();
const S = t.companies;
const router = useRouter();
const { data: session } = useSession();
Expand Down Expand Up @@ -168,7 +169,23 @@ export function CompanyDetail({ company }: { readonly company: CompanyDto }) {
<div className="mt-4">
<div className="flex items-center gap-2">
<h3 className="text-sm font-medium text-oe-gray-mid">{S.detail.description}</h3>
<TranslateButton text={company.description} size="sm" />
<TranslateButton
text={company.description}
size="sm"
configured={configured}
onTranslate={(text, lang) => translateText(text, lang as "de" | "en")}
translations={{
button: t.translation.translate,
dialog: {
title: t.translation.title,
loading: t.translation.loading,
error: t.translation.error,
copy: t.translation.copy,
copied: t.translation.copied,
close: t.translation.close,
},
}}
/>
</div>
<div className="mt-1 text-sm">
<MarkdownView content={company.description} />
Expand Down
29 changes: 23 additions & 6 deletions frontend/src/components/contact-comments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
import { useCallback, useEffect, useState } from "react";
import { useSession } from "next-auth/react";
import { MessageSquarePlus, X } from "lucide-react";
import { Button, DeleteConfirmDialog, Tooltip, TooltipTrigger, TooltipContent, Card, CardContent, CardHeader, CardTitle, Skeleton, MarkdownView } from "@open-elements/ui";
import { Button, DeleteConfirmDialog, Tooltip, TooltipTrigger, TooltipContent, Card, CardContent, CardHeader, CardTitle, Skeleton, MarkdownView, TranslateButton } from "@open-elements/ui";
import { useTranslations } from "@/lib/i18n";
import { AddCommentDialog } from "@/components/add-comment-dialog";
import { TranslateButton } from "@/components/translate-button";
import { getContactComments, createContactComment, deleteContactComment, ForbiddenError } from "@/lib/api";
import { AddCommentDialog } from "@open-elements/nextjs-app-layer";
import { useTranslationConfig } from "@/lib/use-translation-config";
import { getContactComments, createContactComment, deleteContactComment, ForbiddenError, translateText } from "@/lib/api";
import type { CommentDto } from "@/lib/types";
import { hasRole, ROLE_ADMIN } from "@/lib/roles";
import { hasRole, ROLE_ADMIN } from "@open-elements/nextjs-app-layer";

function formatDate(dateString: string, language: string): string {
const date = new Date(dateString);
Expand All @@ -30,6 +30,7 @@ interface ContactCommentsProps {
export function ContactComments({ contactId, totalCount }: ContactCommentsProps) {
const t = useTranslations();
const S = t.companies.comments;
const { configured } = useTranslationConfig();
const { data: session } = useSession();
const canDelete = hasRole(session, ROLE_ADMIN);

Expand Down Expand Up @@ -129,7 +130,23 @@ export function ContactComments({ contactId, totalCount }: ContactCommentsProps)
{comment.author?.name ?? "—"} &middot; {formatDate(comment.createdAt, "de")}
</p>
<div className="flex items-center">
<TranslateButton text={comment.text} size="md" />
<TranslateButton
text={comment.text}
size="md"
configured={configured}
onTranslate={(text, lang) => translateText(text, lang as "de" | "en")}
translations={{
button: t.translation.translate,
dialog: {
title: t.translation.title,
loading: t.translation.loading,
error: t.translation.error,
copy: t.translation.copy,
copied: t.translation.copied,
close: t.translation.close,
},
}}
/>
<Tooltip>
<TooltipTrigger asChild>
<span>
Expand Down
Loading
Loading