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