-
+
@@ -323,6 +487,54 @@ export class SdigWorkspaceCompose extends DeesElement {
return html`${resizeHandles.map((handle) => html`
this.startFieldResize(event, field, handle)}>`)}`;
}
+ private renderSigningRecipient(recipient: IRecipient, orderNumber: number, options: { overlayTop?: number; rowTop?: number; displayRole?: TRecipientRole } = {}): TemplateResult {
+ const initials = recipient.name.split(' ').map((part) => part[0]).slice(0, 2).join('');
+ const isOverlay = options.overlayTop !== undefined;
+ const top = options.overlayTop ?? options.rowTop;
+ const displayRole = options.displayRole || recipient.role;
+ return html`
this.openRecipientContextMenu(event, recipient)} @pointerdown=${!isOverlay ? (event: PointerEvent) => this.startSigningOrderDrag(event, recipient) : undefined}>
${orderNumber}${initials}${recipient.name}
${this.recipientRoleDefinition(displayRole).description}
${this.recipientRoleDefinition(displayRole).shortLabel}`;
+ }
+
+ private renderRoleSection(roleDefinition: typeof recipientRoleDefinitions[number]): TemplateResult {
+ const role = roleDefinition.role;
+ const members = this.recipientsForRole(role);
+ const isTargetRole = this.signingOrderDrag?.targetRole === role;
+ const draggedRecipientId = this.signingOrderDrag?.recipientId;
+ const draggedRecipient = draggedRecipientId !== undefined ? this.recipients.find((recipient) => recipient.id === draggedRecipientId) : undefined;
+
+ if (!this.signingOrderDrag) {
+ return html`
${roleDefinition.label}${members.length}
${roleDefinition.description}
${members.map((recipient, index) => this.renderSigningRecipient(recipient, index + 1))}
`;
+ }
+
+ const visualIndexById = new Map(members.map((recipient, index) => [recipient.id, index]));
+ const overlayTop = isTargetRole ? this.signingOrderDrag.pointerY - this.signingOrderDrag.listTop - this.signingOrderDrag.grabOffsetY : 0;
+ const draggedOrder = draggedRecipient ? (members.findIndex((recipient) => recipient.id === draggedRecipient.id) + 1 || this.signingOrderDrag.targetIndex + 1) : 0;
+
+ return html`
${roleDefinition.label}${members.filter((recipient) => recipient.id !== draggedRecipientId).length + (isTargetRole ? 1 : 0)}
${roleDefinition.description}
+ ${members.filter((recipient) => recipient.id !== draggedRecipientId).map((recipient) => {
+ const visualIndex = visualIndexById.get(recipient.id) ?? 0;
+ return this.renderSigningRecipient(recipient, visualIndex + 1, { rowTop: visualIndex * this.signingOrderDrag!.itemStep, displayRole: role });
+ })}
+ ${isTargetRole ? html`
` : ''}
+ ${isTargetRole && draggedRecipient ? this.renderSigningRecipient(draggedRecipient, draggedOrder, { overlayTop, displayRole: role }) : ''}
+
`;
+ }
+
+ private renderSigningOrder(): TemplateResult {
+ return html`${recipientRoleDefinitions.map((roleDefinition) => this.renderRoleSection(roleDefinition))}`;
+ }
+
+ private renderRecipientContextMenu(): TemplateResult {
+ if (!this.recipientContextMenu) return html``;
+ const recipient = this.recipients.find((currentRecipient) => currentRecipient.id === this.recipientContextMenu?.recipientId);
+ if (!recipient) return html``;
+ const signerCount = this.signingRecipients().length;
+ return html``;
+ }
+
private renderStepper(): TemplateResult {
const labels = ['Upload', 'Place fields', 'Recipients & routing', 'Review & send'];
return html`
@@ -343,12 +555,13 @@ export class SdigWorkspaceCompose extends DeesElement {
${topBar({ breadcrumb: ['signature.digital', 'Inbox', 'Compose'], title: 'Master Services Agreement', subtitle: pill('Draft · auto-saved'), actions: html`${actionButton('Save draft', 'ghost')}${actionButton('Preview', 'outline', 'eye')}${actionButton('Send for signature', 'primary', 'send')}` })}
${this.renderStepper()}
+ ${this.renderRecipientContextMenu()}
Drag onto document
${fieldDefinitions.map((fieldType) => html`
this.startFieldToolDrag(event, fieldType)} @dragend=${() => this.endFieldToolDrag()}>${icon(fieldType.icon, 14)}${fieldType.label}
`)}
Active for
- ${this.recipients.map((recipient) => html`
this.activeRecipient = recipient.id}>${recipient.name.split(' ')[0]}${this.fields.filter((field) => field.recipient === recipient.id).length}
`)}
+ ${this.signingRecipients().map((recipient) => html`
this.activeRecipient = recipient.id}>${recipient.name.split(' ')[0]}${this.fields.filter((field) => field.recipient === recipient.id).length}
`)}
{ event.preventDefault(); if (event.dataTransfer) event.dataTransfer.dropEffect = 'copy'; (event.currentTarget as HTMLElement).classList.add('drag-over'); }} @dragleave=${(event: DragEvent) => (event.currentTarget as HTMLElement).classList.remove('drag-over')} @drop=${(event: DragEvent) => this.addFieldFromDrop(event)}>
@@ -359,8 +572,9 @@ export class SdigWorkspaceCompose extends DeesElement {
${actionButton('Prev', 'outline')}${html`1 / 14`}${actionButton('Next', 'outline')}
-
Signing order · drag to reorder
- ${this.recipients.map((recipient) => html`
this.draggedRecipientId = recipient.id} @dragover=${(event: DragEvent) => event.preventDefault()} @drop=${() => this.reorderRecipient(recipient.id)} @dragend=${() => this.draggedRecipientId = null}>
${recipient.order}${recipient.name.split(' ').map((part) => part[0]).slice(0, 2).join('')}${recipient.name}
${recipient.email}
${icon('more', 12)}
`)}
+
Routing order · drag to reorder
+
Choose who signs, who gets the completed copy, and who is notified at every step.
+ ${this.renderSigningOrder()}
${selectedField ? this.renderFieldEditor(selectedField) : ''}
diff --git a/ts_web/elements/sdig-workspace/sdig-workspace.shared.ts b/ts_web/elements/sdig-workspace/sdig-workspace.shared.ts
index 28b5e9d..09f22e4 100644
--- a/ts_web/elements/sdig-workspace/sdig-workspace.shared.ts
+++ b/ts_web/elements/sdig-workspace/sdig-workspace.shared.ts
@@ -13,6 +13,7 @@ export type TWorkspaceView =
export type TWorkspaceTheme = 'dark' | 'light';
export type TDensity = 'compact' | 'comfortable';
+export type TRecipientRole = 'signer' | 'copy' | 'updates';
export interface IDocumentRow {
id: string;
@@ -31,6 +32,7 @@ export interface IRecipient {
email: string;
color: string;
order: number;
+ role: TRecipientRole;
}
export interface IFieldPlacement {
@@ -55,9 +57,9 @@ export const demoDocuments: IDocumentRow[] = [
];
export const demoRecipients: IRecipient[] = [
- { id: 0, name: 'Sarah Chen', email: 'sarah@acme.com', color: '#60a5fa', order: 1 },
- { id: 1, name: 'David Park', email: 'd.park@acme.com', color: '#fbbf24', order: 2 },
- { id: 2, name: 'Philipp K.', email: 'philipp@lossless.com', color: '#3b82f6', order: 3 },
+ { id: 0, name: 'Sarah Chen', email: 'sarah@acme.com', color: '#60a5fa', order: 1, role: 'signer' },
+ { id: 1, name: 'David Park', email: 'd.park@acme.com', color: '#fbbf24', order: 2, role: 'signer' },
+ { id: 2, name: 'Philipp K.', email: 'philipp@lossless.com', color: '#3b82f6', order: 3, role: 'updates' },
];
export const demoFields: IFieldPlacement[] = [