@@ -18,12 +18,14 @@ type Props<T extends FieldValues = FieldValues> = {
|
||||
label?: string;
|
||||
error?: string;
|
||||
selectedPeople?: Person[];
|
||||
placeholder?: string;
|
||||
} & ComponentProps<typeof Field>;
|
||||
|
||||
export function PeopleMultiSelectField<T extends FieldValues = FieldValues>({
|
||||
organizationId,
|
||||
control,
|
||||
selectedPeople = [],
|
||||
placeholder,
|
||||
...props
|
||||
}: Props<T>) {
|
||||
return (
|
||||
@@ -37,6 +39,7 @@ export function PeopleMultiSelectField<T extends FieldValues = FieldValues>({
|
||||
name={props.name}
|
||||
disabled={props.disabled}
|
||||
selectedPeople={selectedPeople}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</Suspense>
|
||||
</Field>
|
||||
@@ -44,10 +47,10 @@ export function PeopleMultiSelectField<T extends FieldValues = FieldValues>({
|
||||
}
|
||||
|
||||
function PeopleMultiSelectWithQuery<T extends FieldValues = FieldValues>(
|
||||
props: Pick<Props<T>, "organizationId" | "control" | "name" | "disabled" | "selectedPeople">,
|
||||
props: Pick<Props<T>, "organizationId" | "control" | "name" | "disabled" | "selectedPeople" | "placeholder">,
|
||||
) {
|
||||
const { __ } = useTranslate();
|
||||
const { name, organizationId, control, selectedPeople = [] } = props;
|
||||
const { name, organizationId, control, selectedPeople = [], placeholder } = props;
|
||||
const people = usePeople(organizationId, { excludeContractEnded: true });
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
@@ -91,7 +94,7 @@ function PeopleMultiSelectWithQuery<T extends FieldValues = FieldValues>(
|
||||
disabled={props.disabled}
|
||||
id={name}
|
||||
variant="editor"
|
||||
placeholder={__("Add attendees...")}
|
||||
placeholder={placeholder ?? __("Add people...")}
|
||||
onValueChange={handleAddPerson}
|
||||
key={`${selectedPeopleIds.length}-${people.length}`}
|
||||
className="w-full"
|
||||
|
||||
@@ -151,6 +151,7 @@ export function CreateDocumentDialog({ trigger, connection }: Props) {
|
||||
name="approverIds"
|
||||
control={control}
|
||||
organizationId={organizationId}
|
||||
placeholder={__("Add approvers...")}
|
||||
/>
|
||||
</PropertyRow>
|
||||
</div>
|
||||
|
||||
@@ -177,6 +177,7 @@ export function DocumentLayoutDrawer(props: {
|
||||
control={control}
|
||||
organizationId={organizationId}
|
||||
selectedPeople={approvers}
|
||||
placeholder={__("Add approvers...")}
|
||||
/>
|
||||
</EditablePropertyContent>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { formatDate, getDocumentClassificationLabel, getDocumentTypeLabel, sprintf } from "@probo/helpers";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { ActionDropdown, Avatar, Badge, Checkbox, DropdownItem, IconTrashCan, Td, Tr, useConfirm } from "@probo/ui";
|
||||
import { ActionDropdown, Badge, Checkbox, DropdownItem, IconTrashCan, Td, Tr, useConfirm } from "@probo/ui";
|
||||
import { useFragment } from "react-relay";
|
||||
import { type DataID, graphql } from "relay-runtime";
|
||||
|
||||
@@ -132,14 +132,7 @@ export function DocumentListItem(props: {
|
||||
{getDocumentClassificationLabel(__, document.classification)}
|
||||
</Td>
|
||||
<Td className="w-60">
|
||||
<div className="flex gap-2 items-center">
|
||||
{document.approvers.edges.map(({ node }) => (
|
||||
<div key={node.id} className="flex gap-1 items-center">
|
||||
<Avatar name={node.fullName} />
|
||||
{node.fullName}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{document.approvers.edges.map(({ node }) => node.fullName).join(", ")}
|
||||
</Td>
|
||||
<Td className="w-60">{formatDate(document.updatedAt)}</Td>
|
||||
<Td className="w-20">
|
||||
|
||||
@@ -104,6 +104,7 @@ export function CreateMeetingDialog({ children, connectionId }: Props) {
|
||||
control={control}
|
||||
organizationId={organizationId}
|
||||
label={__("Attendees")}
|
||||
placeholder={__("Add attendees...")}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
|
||||
@@ -204,7 +204,7 @@ type (
|
||||
Content string
|
||||
Version int
|
||||
Classification Classification
|
||||
Approver string
|
||||
Approvers []string
|
||||
Description string
|
||||
PublishedAt *time.Time
|
||||
Signatures []SignatureData
|
||||
|
||||
@@ -41,7 +41,7 @@ func TestRenderHTML(t *testing.T) {
|
||||
Content: "# Main Title\n\nThis is **bold** text with *italic* formatting.",
|
||||
Version: 1,
|
||||
Classification: ClassificationPublic,
|
||||
Approver: "John Doe",
|
||||
Approvers: []string{"John Doe"},
|
||||
PublishedAt: &now,
|
||||
Signatures: []SignatureData{
|
||||
{
|
||||
@@ -66,9 +66,9 @@ func TestRenderHTML(t *testing.T) {
|
||||
{
|
||||
name: "document with HTML characters that need escaping",
|
||||
data: DocumentData{
|
||||
Title: "Test & <Script> Title",
|
||||
Content: "Normal markdown content",
|
||||
Approver: "John <script>alert('xss')</script> Doe",
|
||||
Title: "Test & <Script> Title",
|
||||
Content: "Normal markdown content",
|
||||
Approvers: []string{"John <script>alert('xss')</script> Doe"},
|
||||
Signatures: []SignatureData{
|
||||
{
|
||||
SignedBy: "Alice & <Bob>",
|
||||
@@ -253,8 +253,8 @@ func TestClassificationConstants(t *testing.T) {
|
||||
|
||||
func TestHTMLEscaping(t *testing.T) {
|
||||
dangerousData := DocumentData{
|
||||
Title: "<script>alert('xss')</script>",
|
||||
Approver: "User & <Company>",
|
||||
Title: "<script>alert('xss')</script>",
|
||||
Approvers: []string{"User & <Company>"},
|
||||
Signatures: []SignatureData{
|
||||
{
|
||||
SignedBy: "<malicious>tag",
|
||||
@@ -385,7 +385,7 @@ func BenchmarkGenerateHTML(b *testing.B) {
|
||||
Content: "# Title\n\nThis is **bold** text with *italic* formatting.\n\n- Item 1\n- Item 2",
|
||||
Version: 1,
|
||||
Classification: ClassificationPublic,
|
||||
Approver: "John Doe",
|
||||
Approvers: []string{"John Doe"},
|
||||
PublishedAt: &now,
|
||||
Signatures: []SignatureData{
|
||||
{
|
||||
|
||||
@@ -363,8 +363,18 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Approver</td>
|
||||
<td>{{.Approver}}</td>
|
||||
<td>Approver{{- if gt (len .Approvers) 1}}s{{- end}}</td>
|
||||
<td>
|
||||
{{- if eq (len .Approvers) 1}}
|
||||
{{index .Approvers 0}}
|
||||
{{- else}}
|
||||
<ul style="margin: 0; padding-left: 18px;">
|
||||
{{- range .Approvers}}
|
||||
<li>{{.}}</li>
|
||||
{{- end}}
|
||||
</ul>
|
||||
{{- end}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Version:</td>
|
||||
|
||||
@@ -1927,7 +1927,7 @@ func exportDocumentPDF(
|
||||
Content: version.Content,
|
||||
Version: version.VersionNumber,
|
||||
Classification: classification,
|
||||
Approver: strings.Join(approverNames, ", "),
|
||||
Approvers: approverNames,
|
||||
PublishedAt: version.PublishedAt,
|
||||
Signatures: signatureData,
|
||||
CompanyHorizontalLogoBase64: horizontalLogoBase64,
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"go.gearno.de/kit/pg"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
@@ -215,7 +214,7 @@ func (s *DocumentService) exportPDFData(
|
||||
Content: version.Content,
|
||||
Version: version.VersionNumber,
|
||||
Classification: classification,
|
||||
Approver: strings.Join(approverNames, ", "),
|
||||
Approvers: approverNames,
|
||||
PublishedAt: version.PublishedAt,
|
||||
CompanyHorizontalLogoBase64: horizontalLogoBase64,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user