mirror of
https://github.com/documenso/documenso.git
synced 2025-11-10 04:22:32 +10:00
chore: add translations
This commit is contained in:
@ -91,7 +91,7 @@ export function DocumentsDataTable({
|
||||
const columns = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
header: 'Created',
|
||||
header: _(msg`Created`),
|
||||
accessorKey: 'createdAt',
|
||||
cell: ({ row }) =>
|
||||
i18n.date(row.original.createdAt, { ...DateTime.DATETIME_SHORT, hourCycle: 'h12' }),
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Plural, Trans } from '@lingui/react/macro';
|
||||
import type { Table } from '@tanstack/react-table';
|
||||
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';
|
||||
@ -21,6 +23,8 @@ export function DataTablePagination<TData>({
|
||||
table,
|
||||
additionalInformation = 'VisibleCount',
|
||||
}: DataTablePaginationProps<TData>) {
|
||||
const { _ } = useLingui();
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap items-center justify-between gap-x-4 gap-y-4 px-2">
|
||||
<div className="text-muted-foreground flex-1 text-sm">
|
||||
@ -86,7 +90,7 @@ export function DataTablePagination<TData>({
|
||||
onClick={() => table.setPageIndex(0)}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className="sr-only">Go to first page</span>
|
||||
<span className="sr-only">{_(msg`Go to first page`)}</span>
|
||||
<ChevronsLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
@ -95,7 +99,7 @@ export function DataTablePagination<TData>({
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className="sr-only">Go to previous page</span>
|
||||
<span className="sr-only">{_(msg`Go to previous page`)}</span>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
@ -104,7 +108,7 @@ export function DataTablePagination<TData>({
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className="sr-only">Go to next page</span>
|
||||
<span className="sr-only">{_(msg`Go to next page`)}</span>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
@ -113,7 +117,7 @@ export function DataTablePagination<TData>({
|
||||
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className="sr-only">Go to last page</span>
|
||||
<span className="sr-only">{_(msg`Go to last page`)} </span>
|
||||
<ChevronsRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import type { MessageDescriptor } from '@lingui/core';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import type { Column } from '@tanstack/react-table';
|
||||
import { Check } from 'lucide-react';
|
||||
|
||||
@ -18,7 +21,7 @@ interface DataTableFacetedFilterProps<TData, TValue> {
|
||||
onFilterChange?: (values: string[]) => void;
|
||||
selectedValues?: string[];
|
||||
options: {
|
||||
label: string;
|
||||
label: MessageDescriptor;
|
||||
value: string;
|
||||
icon?: React.ComponentType<{ className?: string }>;
|
||||
color?: string;
|
||||
@ -35,6 +38,7 @@ export function DataTableFacetedFilter<TData, TValue>({
|
||||
selectedValues,
|
||||
options,
|
||||
}: DataTableFacetedFilterProps<TData, TValue>) {
|
||||
const { _ } = useLingui();
|
||||
const facets = column?.getFacetedUniqueValues();
|
||||
const selectedValuesSet = new Set(selectedValues || (column?.getFilterValue() as string[]));
|
||||
|
||||
@ -53,7 +57,7 @@ export function DataTableFacetedFilter<TData, TValue>({
|
||||
<div className="hidden gap-1 lg:flex">
|
||||
{selectedValuesSet.size > 2 ? (
|
||||
<Badge variant="neutral" className="rounded-sm px-2 py-0.5 font-normal">
|
||||
{selectedValuesSet.size} selected
|
||||
{selectedValuesSet.size} {_(msg`selected`)}
|
||||
</Badge>
|
||||
) : (
|
||||
options
|
||||
@ -67,7 +71,7 @@ export function DataTableFacetedFilter<TData, TValue>({
|
||||
option.bgColor ? option.bgColor : 'bg-secondary',
|
||||
)}
|
||||
>
|
||||
{option.label}
|
||||
{_(option.label)}
|
||||
</Badge>
|
||||
))
|
||||
)}
|
||||
@ -79,7 +83,7 @@ export function DataTableFacetedFilter<TData, TValue>({
|
||||
<PopoverContent className="w-[200px] p-0" align="start">
|
||||
<Command>
|
||||
<CommandList>
|
||||
<CommandEmpty>No results found.</CommandEmpty>
|
||||
<CommandEmpty>{_(msg`No results found.`)}</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{options.map((option) => {
|
||||
const isSelected = selectedValuesSet.has(option.value);
|
||||
@ -120,7 +124,7 @@ export function DataTableFacetedFilter<TData, TValue>({
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<span>{option.label}</span>
|
||||
<span>{_(option.label)}</span>
|
||||
{(stats?.[option.value] || facets?.get(option.value)) && (
|
||||
<span className="text-muted-foreground ml-auto flex size-4 items-center justify-center font-mono text-xs">
|
||||
{stats?.[option.value] || facets?.get(option.value)}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import type { MessageDescriptor } from '@lingui/core';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import type { Column } from '@tanstack/react-table';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
@ -22,14 +24,14 @@ interface DataTableSingleFilterProps<TData, TValue> {
|
||||
onFilterChange?: (values: string[]) => void;
|
||||
selectedValues?: string[];
|
||||
options: {
|
||||
label: string;
|
||||
label: MessageDescriptor;
|
||||
value: string;
|
||||
icon?: React.ComponentType<{ className?: string }>;
|
||||
color?: string;
|
||||
bgColor?: string;
|
||||
}[];
|
||||
groups?: {
|
||||
label: string;
|
||||
label: MessageDescriptor;
|
||||
values: string[];
|
||||
}[];
|
||||
}
|
||||
@ -43,6 +45,7 @@ export function DataTableSingleFilter<TData, TValue>({
|
||||
onFilterChange,
|
||||
selectedValues,
|
||||
}: DataTableSingleFilterProps<TData, TValue>) {
|
||||
const { _ } = useLingui();
|
||||
const filterValue = column?.getFilterValue() as string[] | undefined;
|
||||
const selectedValue = selectedValues?.[0] || (filterValue?.[0] ?? undefined);
|
||||
const selectedOption = options.find((option) => option.value === selectedValue);
|
||||
@ -66,9 +69,9 @@ export function DataTableSingleFilter<TData, TValue>({
|
||||
const renderOptions = () => {
|
||||
if (groups) {
|
||||
return groups.map((group, groupIndex) => (
|
||||
<React.Fragment key={group.label}>
|
||||
<React.Fragment key={JSON.stringify(group.label)}>
|
||||
<SelectGroup>
|
||||
<SelectLabel>{group.label}</SelectLabel>
|
||||
<SelectLabel>{_(group.label)}</SelectLabel>
|
||||
{options
|
||||
.filter((option) => group.values.includes(option.value))
|
||||
.map((option) => (
|
||||
@ -82,7 +85,7 @@ export function DataTableSingleFilter<TData, TValue>({
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<span>{option.label}</span>
|
||||
<span>{_(option.label)}</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
@ -102,7 +105,7 @@ export function DataTableSingleFilter<TData, TValue>({
|
||||
className={cn('size-4', option.color ? option.color : 'text-muted-foreground')}
|
||||
/>
|
||||
)}
|
||||
<span>{option.label}</span>
|
||||
<span>{_(option.label)}</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
@ -125,7 +128,7 @@ export function DataTableSingleFilter<TData, TValue>({
|
||||
selectedOption.bgColor ? selectedOption.bgColor : 'variant-secondary',
|
||||
)}
|
||||
>
|
||||
{selectedOption.label}
|
||||
{_(selectedOption.label)}
|
||||
</Badge>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import type { Table } from '@tanstack/react-table';
|
||||
import { Calendar, CircleDashedIcon, Globe, ListFilter, X, XCircle } from 'lucide-react';
|
||||
|
||||
@ -36,6 +38,7 @@ export function DataTableToolbar<TData>({
|
||||
isTimePeriodFiltered,
|
||||
isSourceFiltered,
|
||||
}: DataTableToolbarProps<TData>) {
|
||||
const { _ } = useLingui();
|
||||
const isFiltered =
|
||||
table.getState().columnFilters.length > 0 ||
|
||||
isStatusFiltered ||
|
||||
@ -58,7 +61,7 @@ export function DataTableToolbar<TData>({
|
||||
<div className="relative">
|
||||
<Input
|
||||
className="peer h-8 w-[150px] pe-9 ps-9 lg:w-[250px]"
|
||||
placeholder="Search documents..."
|
||||
placeholder={_(msg`Search documents...`)}
|
||||
value={searchValue}
|
||||
onChange={(event) => table.getColumn('title')?.setFilterValue(event.target.value)}
|
||||
/>
|
||||
@ -68,7 +71,7 @@ export function DataTableToolbar<TData>({
|
||||
{searchValue && (
|
||||
<button
|
||||
className="text-muted-foreground/80 hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 absolute inset-y-0 end-0 flex h-full w-9 items-center justify-center rounded-e-md outline-none transition-[color,box-shadow] focus:z-10 focus-visible:ring-[3px] disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50"
|
||||
aria-label="Clear filter"
|
||||
aria-label={_(msg`Clear filter`)}
|
||||
onClick={handleClearFilter}
|
||||
>
|
||||
<XCircle className="size-3" aria-hidden="true" />
|
||||
@ -79,7 +82,7 @@ export function DataTableToolbar<TData>({
|
||||
{table.getColumn('status') && (
|
||||
<DataTableFacetedFilter
|
||||
column={table.getColumn('status')}
|
||||
title="Status"
|
||||
title={_(msg`Status`)}
|
||||
options={statuses}
|
||||
icon={CircleDashedIcon}
|
||||
stats={stats}
|
||||
@ -91,7 +94,7 @@ export function DataTableToolbar<TData>({
|
||||
{table.getColumn('createdAt') && (
|
||||
<DataTableSingleFilter
|
||||
column={table.getColumn('createdAt')}
|
||||
title="Time Period"
|
||||
title={_(msg`Time Period`)}
|
||||
options={timePeriods}
|
||||
groups={timePeriodGroups}
|
||||
icon={Calendar}
|
||||
@ -103,7 +106,7 @@ export function DataTableToolbar<TData>({
|
||||
{table.getColumn('source') && (
|
||||
<DataTableFacetedFilter
|
||||
column={table.getColumn('source')}
|
||||
title="Source"
|
||||
title={_(msg`Source`)}
|
||||
options={sources}
|
||||
icon={Globe}
|
||||
onFilterChange={onSourceFilterChange}
|
||||
@ -113,7 +116,7 @@ export function DataTableToolbar<TData>({
|
||||
|
||||
{isFiltered && (
|
||||
<Button variant="ghost" className="h-8 gap-2" size="sm" onClick={handleReset}>
|
||||
Reset
|
||||
{_(msg`Reset`)}
|
||||
<X className="size-4" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type {
|
||||
ColumnDef,
|
||||
@ -83,6 +85,7 @@ export function DataTable<TData, TValue>({
|
||||
isSourceFiltered,
|
||||
emptyState,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const { _ } = useLingui();
|
||||
const [rowSelection, setRowSelection] = React.useState({});
|
||||
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
|
||||
@ -211,10 +214,14 @@ export function DataTable<TData, TValue>({
|
||||
</div>
|
||||
) : emptyState?.enable ? (
|
||||
(emptyState.component ?? (
|
||||
<div className="flex h-24 items-center justify-center text-center">No results.</div>
|
||||
<div className="flex h-24 items-center justify-center text-center">
|
||||
{_(msg`No results.`)}
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="flex h-24 items-center justify-center text-center">No results.</div>
|
||||
<div className="flex h-24 items-center justify-center text-center">
|
||||
{_(msg`No results.`)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,37 +1,38 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { CheckCircle2, Clock, File, FileText, Inbox, Link, XCircle } from 'lucide-react';
|
||||
|
||||
export const statuses = [
|
||||
{
|
||||
value: 'INBOX',
|
||||
label: 'Inbox',
|
||||
label: msg`Inbox`,
|
||||
icon: Inbox,
|
||||
color: 'text-blue-700 dark:text-blue-300',
|
||||
bgColor: 'bg-blue-100 dark:bg-blue-100 text-blue-700 dark:text-blue-700',
|
||||
},
|
||||
{
|
||||
value: 'DRAFT',
|
||||
label: 'Draft',
|
||||
label: msg`Draft`,
|
||||
icon: File,
|
||||
color: 'text-yellow-500 dark:text-yellow-300',
|
||||
bgColor: 'bg-yellow-100 dark:bg-yellow-100 text-yellow-700 dark:text-yellow-700',
|
||||
},
|
||||
{
|
||||
value: 'PENDING',
|
||||
label: 'Pending',
|
||||
label: msg`Pending`,
|
||||
icon: Clock,
|
||||
color: 'text-blue-700 dark:text-blue-300',
|
||||
bgColor: 'bg-blue-100 dark:bg-blue-100 text-blue-700 dark:text-blue-700',
|
||||
},
|
||||
{
|
||||
value: 'COMPLETED',
|
||||
label: 'Completed',
|
||||
label: msg`Completed`,
|
||||
icon: CheckCircle2,
|
||||
color: 'text-documenso-700 dark:text-documenso-300',
|
||||
bgColor: 'bg-documenso-200 dark:bg-documenso-200 text-documenso-800 dark:text-documenso-800',
|
||||
},
|
||||
{
|
||||
value: 'REJECTED',
|
||||
label: 'Rejected',
|
||||
label: msg`Rejected`,
|
||||
icon: XCircle,
|
||||
color: 'text-red-700 dark:text-red-300',
|
||||
bgColor: 'bg-red-100 dark:bg-red-100 text-red-500 dark:text-red-700',
|
||||
@ -41,14 +42,14 @@ export const statuses = [
|
||||
export const sources = [
|
||||
{
|
||||
value: 'TEMPLATE',
|
||||
label: 'Template',
|
||||
label: msg`Template`,
|
||||
icon: FileText,
|
||||
color: 'text-blue-700 dark:text-blue-300',
|
||||
bgColor: 'bg-blue-100 dark:bg-blue-100 text-blue-700 dark:text-blue-700',
|
||||
},
|
||||
{
|
||||
value: 'DIRECT_LINK',
|
||||
label: 'Direct Link',
|
||||
label: msg`Direct Link`,
|
||||
icon: Link,
|
||||
color: 'text-green-700 dark:text-green-300',
|
||||
bgColor: 'bg-green-100 dark:bg-green-100 text-green-700 dark:text-green-700',
|
||||
@ -58,61 +59,61 @@ export const sources = [
|
||||
export const timePeriods = [
|
||||
{
|
||||
value: 'today',
|
||||
label: 'Today',
|
||||
label: msg`Today`,
|
||||
},
|
||||
{
|
||||
value: 'this-week',
|
||||
label: 'This Week',
|
||||
label: msg`This Week`,
|
||||
},
|
||||
{
|
||||
value: 'this-month',
|
||||
label: 'This Month',
|
||||
label: msg`This Month`,
|
||||
},
|
||||
{
|
||||
value: 'this-quarter',
|
||||
label: 'This Quarter',
|
||||
label: msg`This Quarter`,
|
||||
},
|
||||
{
|
||||
value: 'this-year',
|
||||
label: 'This Year',
|
||||
label: msg`This Year`,
|
||||
},
|
||||
{
|
||||
value: 'yesterday',
|
||||
label: 'Yesterday',
|
||||
label: msg`Yesterday`,
|
||||
},
|
||||
{
|
||||
value: 'last-week',
|
||||
label: 'Last Week',
|
||||
label: msg`Last Week`,
|
||||
},
|
||||
{
|
||||
value: 'last-month',
|
||||
label: 'Last Month',
|
||||
label: msg`Last Month`,
|
||||
},
|
||||
{
|
||||
value: 'last-quarter',
|
||||
label: 'Last Quarter',
|
||||
label: msg`Last Quarter`,
|
||||
},
|
||||
{
|
||||
value: 'last-year',
|
||||
label: 'Last Year',
|
||||
label: msg`Last Year`,
|
||||
},
|
||||
{
|
||||
value: 'all-time',
|
||||
label: 'All Time',
|
||||
label: msg`All Time`,
|
||||
},
|
||||
];
|
||||
|
||||
export const timePeriodGroups = [
|
||||
{
|
||||
label: 'Present',
|
||||
label: msg`Present`,
|
||||
values: ['today', 'this-week', 'this-month', 'this-quarter', 'this-year'],
|
||||
},
|
||||
{
|
||||
label: 'Past',
|
||||
label: msg`Past`,
|
||||
values: ['yesterday', 'last-week', 'last-month', 'last-quarter', 'last-year'],
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
label: msg``,
|
||||
values: ['all-time'],
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user