@verdocs/js-sdk 4.0.4 → 4.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -443,2168 +443,2177 @@ class VerdocsEndpoint {
443
443
  }
444
444
 
445
445
  /**
446
- * Create an envelope
447
- *
448
- * ```typescript
449
- * import {Envelopes, ICreateEnvelopeRole, ICreateEnvelopeRequest} from '@verdocs/js-sdk/Envelopes';
450
- *
451
- * const role1: ICreateEnvelopeRole = {
452
- * type: 'signer',
453
- * name: 'Seller',
454
- * full_name: 'Paige Turner',
455
- * email: 'paige.turner@nomail.com',
456
- * phone: '',
457
- * sequence: 1,
458
- * delegator: false,
459
- * message: '',
460
- * };
461
- *
462
- * const role2: ICreateEnvelopeRole = {
463
- * type: 'signer',
464
- * name: 'Buyer',
465
- * full_name: 'Will Power',
466
- * email: 'will.power@nomail.com',
467
- * phone: '',
468
- * sequence: 2,
469
- * delegator: false,
470
- * message: '',
471
- * };
472
- *
473
- * const request: ICreateEnvelopeRequest = {template_id: 'd2338742-f3a1-465b-8592-806587413cc1', name: 'Bill of Sale', roles: [role1, role2]};
474
- * const {id, recipients} = await Envelopes.createEnvelope(VerdocsEndpoint.getDefault(), request);
475
- * ```
476
- */
477
- const createEnvelope = async (endpoint, request) => endpoint.api //
478
- .post('/envelopes', request)
479
- .then((r) => r.data);
480
- /**
481
- * Get a summary of currently active envelopes.
482
- *
483
- * ```typescript
484
- * import {Envelopes} from '@verdocs/js-sdk/Envelopes';
485
- *
486
- * const {action_required, completed, waiting_on_others} = await Envelopes.getSummary(VerdocsEndpoint.getDefault());
487
- * ```
488
- */
489
- const getEnvelopesSummary = async (endpoint, page) => endpoint.api //
490
- .post('/envelopes/summary', { page })
491
- .then((r) => r.data);
492
- /**
493
- * Search for envelopes matching various criteria.
494
- *
495
- * ```typescript
496
- * import {Envelopes} from '@verdocs/js-sdk/Envelopes';
497
- *
498
- * const {result, page, total} = await Envelopes.search(VerdocsEndpoint.getDefault(), { ... });
499
- * ```
500
- */
501
- const searchEnvelopes = async (endpoint, params) => endpoint.api //
502
- .post('/envelopes/search', params)
503
- .then((r) => r.data);
504
- /**
505
- * Get a signing session for an Envelope.
506
- */
507
- const getSigningSession = async (endpoint, params) => {
508
- window.console.log('[JS_SDK] getSigningSession', params, endpoint.api);
509
- return endpoint.api //
510
- .get(`/envelopes/${params.envelopeId}/recipients/${encodeURIComponent(params.roleId)}/invitation/${params.inviteCode}`)
511
- .then((r) => {
512
- // Avoiding a jsonwebtoken dependency here - we don't actually need the whole library
513
- const signerToken = r.headers?.signer_token || '';
514
- const session = decodeAccessTokenBody(signerToken);
515
- endpoint.setToken(signerToken);
516
- return { recipient: r.data, session, signerToken };
517
- });
518
- };
519
- /**
520
- * Get the list of recipients for an Envelope.
521
- */
522
- const getEnvelopeRecipients = async (endpoint, envelopeId) => endpoint.api //
523
- .get(`/envelopes/${envelopeId}/recipients`)
524
- .then((r) => r.data);
525
- /**
526
- * Get all metadata for an Envelope.
527
- */
528
- const getEnvelope = async (endpoint, envelopeId) => endpoint.api //
529
- .get(`/envelopes/${envelopeId}`)
530
- .then((r) => r.data);
531
- /**
532
- * Get an Envelope Document
533
- */
534
- const getEnvelopeDocument = async (endpoint, envelopeId, documentId) => endpoint.api //
535
- .get(`/envelopes/${envelopeId}/envelope_documents/${documentId}`)
536
- .then((r) => r.data);
537
- /**
538
- * Get a pre-signed download link for an Envelope Document. This link expires quickly, so it should
539
- * be accessed immediately and never shared. Content-Disposition will be set to "download".
540
- */
541
- const getDocumentDownloadLink = async (endpoint, envelopeId, documentId) => endpoint.api //
542
- .get(`/envelopes/${envelopeId}/envelope_documents/${documentId}?download=true`)
543
- .then((r) => r.data);
544
- /**
545
- * Get a pre-signed preview link for an Envelope Document. This link expires quickly, so it should
546
- * be accessed immediately and never shared. Content-Disposition will be set to "inline".
547
- */
548
- const getDocumentPreviewLink = async (endpoint, envelopeId, documentId) => endpoint.api //
549
- .get(`/envelopes/${envelopeId}/envelope_documents/${documentId}?preview=true`)
550
- .then((r) => r.data);
551
- /**
552
- * Cancel an Envelope.
553
- */
554
- const cancelEnvelope = async (endpoint, envelopeId) => endpoint.api //
555
- .put(`/envelopes/${envelopeId}`, { action: 'cancel' })
556
- .then((r) => r.data);
557
- /**
558
- * Get (binary download) a file attached to an Envelope. It is important to use this method
559
- * rather than a direct A HREF or similar link to set the authorization headers for the
560
- * request.
561
- */
562
- const getEnvelopeFile = async (endpoint, envelopeId, documentId) => endpoint.api //
563
- .get(`/envelopes/${envelopeId}/envelope_documents/${documentId}?file=true`, { responseType: 'blob' })
564
- .then((r) => r.data);
565
- /**
566
- * Update a Document field. Typically called during the signing process as a Recipient fills in fields.
567
- */
568
- const updateEnvelopeField = async (endpoint, envelopeId, fieldName, value) => endpoint.api //
569
- .put(`/envelopes/${envelopeId}/fields/${fieldName}`, value)
570
- .then((r) => r.data);
571
- /**
572
- * Update a Document signature field. Signature fields are ID-driven. Call `Document.createSignature()` first to create a
573
- * signature for a Recipient, then call `Documents.updateDocumentFieldSignature()` to attach it to a field.
574
- */
575
- const updateEnvelopeFieldSignature = async (endpoint, envelopeId, fieldName, signatureId) => endpoint.api //
576
- .put(`/envelopes/${envelopeId}/fields/${fieldName}/signature/${signatureId}`)
577
- .then((r) => r.data);
578
- /**
579
- * Update a Document signature field. Signature fields are ID-driven. Call `Document.createSignature()` first to create a
580
- * signature for a Recipient, then call `Documents.updateDocumentFieldSignature()` to attach it to a field.
446
+ * Given a `rgba(r,g,b,a)` string value, returns the hex equivalent, dropping the alpha channel.
581
447
  */
582
- const updateEnvelopeFieldInitials = async (endpoint, envelopeId, fieldName, initialId) => endpoint.api //
583
- .put(`/envelopes/${envelopeId}/fields/${fieldName}/initial/${initialId}`)
584
- .then((r) => r.data);
448
+ function getRGB(rgba) {
449
+ const rgbNumbers = rgba.replace('rgba(', '').replace(')', '').split(',');
450
+ const rgbObject = {
451
+ red: +rgbNumbers[0],
452
+ green: +rgbNumbers[1],
453
+ blue: +rgbNumbers[2],
454
+ alpha: +rgbNumbers[3],
455
+ };
456
+ const alpha = 1 - rgbObject.alpha;
457
+ const red = Math.round((rgbObject.alpha * (rgbObject.red / 255) + alpha) * 255);
458
+ const green = Math.round((rgbObject.alpha * (rgbObject.green / 255) + alpha) * 255);
459
+ const blue = Math.round((rgbObject.alpha * (rgbObject.blue / 255) + alpha) * 255);
460
+ return '#' + rgbToHex(red) + rgbToHex(green) + rgbToHex(blue);
461
+ }
585
462
  /**
586
- * Upload an attachment.
463
+ * Given an RGB string value, returns the hex equivalent.
587
464
  */
588
- const uploadEnvelopeFieldAttachment = async (endpoint, envelopeId, fieldName, file, onUploadProgress) => {
589
- const formData = new FormData();
590
- formData.append('document', file, file.name);
591
- return endpoint.api //
592
- .put(`/envelopes/${envelopeId}/fields/${fieldName}`, formData, {
593
- timeout: 120000,
594
- onUploadProgress: (event) => {
595
- const total = event.total || 1;
596
- const loaded = event.loaded || 0;
597
- onUploadProgress?.(Math.floor((loaded * 100) / (total || 1)), loaded, total || 1);
598
- },
599
- })
600
- .then((r) => r.data);
601
- };
465
+ function rgbToHex(rgb) {
466
+ const hex = rgb.toString(16);
467
+ if (hex.length < 2) {
468
+ return '0' + hex;
469
+ }
470
+ return hex;
471
+ }
602
472
  /**
603
- * Delete an attachment.
473
+ * Given a signer role index, return the color code for that signer.
604
474
  */
605
- const deleteEnvelopeFieldAttachment = async (endpoint, envelopeId, fieldName, file, onUploadProgress) => {
606
- const formData = new FormData();
607
- // Omitting file is the trigger here
608
- return endpoint.api //
609
- .put(`/envelopes/${envelopeId}/fields/${fieldName}`, formData, {
610
- timeout: 120000,
611
- onUploadProgress: (event) => {
612
- const total = event.total || 1;
613
- const loaded = event.loaded || 0;
614
- onUploadProgress?.(Math.floor((loaded * 100) / (total || 1)), loaded, total || 1);
615
- },
616
- })
617
- .then((r) => r.data);
618
- };
475
+ function getRGBA(roleIndex) {
476
+ switch (roleIndex % 10) {
477
+ case 0:
478
+ return roleIndex === 0 ? 'rgba(255, 193, 7, 0.4)' : 'rgba(134, 134, 134, 0.3)'; // #FFE69C
479
+ case 1:
480
+ return 'rgba(156, 39, 176, .4)'; // '#E3C3E9'
481
+ case 2:
482
+ return 'rgba(33, 150, 243, .4)'; // '#C1E1FB'
483
+ case 3:
484
+ return 'rgba(220, 231, 117, 0.3)';
485
+ case 4:
486
+ return 'rgba(121, 134, 203, 0.3)';
487
+ case 5:
488
+ return 'rgba(77, 182, 172, 0.3)';
489
+ case 6:
490
+ return 'rgba(255, 202, 165, 0.3)';
491
+ case 7:
492
+ return 'rgba(2, 247, 190, 0.3)';
493
+ case 8:
494
+ return 'rgba(255, 138, 101, 0.3)';
495
+ case 9:
496
+ return 'rgba(82, 255, 79, 0.3)';
497
+ default:
498
+ return 'rgba(229, 115, 155, 0.3)';
499
+ }
500
+ }
619
501
  /**
620
- * Get the attached file for an attachment field (if any)
502
+ * Given a role name, return a color code for it. This works by computing a hash code so the specific color returned
503
+ * is not specified explicitly, but will be the same for every call with the same input value.
621
504
  */
622
- const getFieldAttachment = async (endpoint, envelopeId, fieldName) => endpoint.api //
623
- .get(`/envelopes/${envelopeId}/fields/${fieldName}/document`, { responseType: 'blob' })
624
- .then((r) => r.data);
505
+ function nameToRGBA(str) {
506
+ if (!!str) {
507
+ const validNum = parseInt(str.slice(-1), 10);
508
+ if (!isNaN(validNum)) {
509
+ str += (validNum * 99).toString();
510
+ }
511
+ let hash = 0;
512
+ for (let i = 0; i < str.length; i++) {
513
+ // tslint:disable-next-line:no-bitwise
514
+ hash = str.charCodeAt(i) + ((hash << 5) - hash);
515
+ }
516
+ hash = Math.round(hash / 1.3);
517
+ // tslint:disable-next-line:no-bitwise
518
+ const c = (hash & 0x00ffff08).toString(16).toUpperCase();
519
+ const hex = '#' + '00000'.substring(0, 6 - c.length) + c;
520
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
521
+ const color = {
522
+ r: parseInt(result[1], 16),
523
+ g: parseInt(result[2], 16),
524
+ b: parseInt(result[3], 16),
525
+ };
526
+ return `rgba(${color.r}, ${color.g}, ${color.b}, 0.2)`;
527
+ }
528
+ }
625
529
  /**
626
- * Get a display URI for a given page in a file attached to an envelope document. These pages are rendered server-side
627
- * into PNG resources suitable for display in IMG tags although they may be used elsewhere. Note that these are intended
628
- * for DISPLAY ONLY, are not legally binding documents, and do not contain any encoded metadata from participants.
530
+ * Helper function to obtain a color code given a role name given various possible inputs.
629
531
  */
630
- const getEnvelopeDocumentPageDisplayUri = async (endpoint, envelopeId, documentId, page, type = 'original') => endpoint.api
631
- .get(`/envelopes/${envelopeId}/envelope_documents/${documentId}/pages/${page}/image?type=${type}`, { timeout: 20000 })
632
- .then((r) => r.data);
633
- const cachedEnvelopes = {};
634
- /**
635
- * Wrapper for `getEnvelope()` that limits queries to one every 2 seconds per template ID.
636
- * This is intended for use in component hierarchies that all rely on the same template
637
- * to avoid unnecessary repeat server calls.
638
- */
639
- const throttledGetEnvelope = (endpoint, envelopeId) => {
640
- if (cachedEnvelopes[envelopeId] && cachedEnvelopes[envelopeId].loaded + 2000 < new Date().getTime()) {
641
- return cachedEnvelopes[envelopeId].envelope;
532
+ function getRoleColor(name, roles, index) {
533
+ if (index) {
534
+ return getRGBA(index);
642
535
  }
643
- return getEnvelope(endpoint, envelopeId).then((envelope) => {
644
- cachedEnvelopes[envelopeId] = { loaded: new Date().getTime(), envelope };
645
- return envelope;
646
- });
647
- };
648
- /**a
649
- * Lists all envelopes accessible by the caller, with optional filters.
650
- *
651
- * ```typescript
652
- * import {Envelopes} from '@verdocs/js-sdk/Envelopes';
653
- *
654
- * const {totals, envelopes} = await Envelopes.listEnvelopes((VerdocsEndpoint.getDefault(), { q: 'test', sort: 'created_at' });
655
- * ```
656
- */
657
- const listEnvelopes = (endpoint, params) => endpoint.api //
658
- .post('/envelopes/list', params)
659
- .then((r) => r.data);
536
+ else if (roles && roles.length > 0) {
537
+ const roleIndex = roles.findIndex((role) => role === name);
538
+ if (roleIndex > -1) {
539
+ return getRGBA(roleIndex);
540
+ }
541
+ else {
542
+ return nameToRGBA(name);
543
+ }
544
+ }
545
+ else {
546
+ return nameToRGBA(name);
547
+ }
548
+ }
660
549
 
661
- /**
662
- * Create an initials block. In a typical signing workflow, the user is asked at the beginning of the process to "adopt"
663
- * an initials block to be used for all initials fields in the document. Thus, this is typically called one time to
664
- * create and store an initials block. Thereafter, the ID of the initials block may be re-used for each initials field
665
- * to be "stamped" by the user.
666
- */
667
- const createInitials = (endpoint, name, initials) => {
668
- const data = new FormData();
669
- data.append('initial', initials, name);
670
- return endpoint.api //
671
- .post(`/initials`, data)
672
- .then((r) => r.data);
550
+ const YEAR = 365 * 24 * 60 * 60;
551
+ // const MONTH = 30 * 24 * 60 * 60;
552
+ const WEEK = 7 * 24 * 60 * 60;
553
+ const DAY = 24 * 60 * 60;
554
+ const HOUR = 60 * 60;
555
+ const MINUTE = 60;
556
+ const formatShortTimeAgo = (val) => {
557
+ if (val === undefined || val === null) {
558
+ return '';
559
+ }
560
+ let dateInput;
561
+ if (typeof val === 'string' || typeof val === 'number') {
562
+ dateInput = new Date(val);
563
+ }
564
+ else if (typeof val === 'object') {
565
+ dateInput = val;
566
+ }
567
+ else {
568
+ return '';
569
+ }
570
+ const timeDiff = Math.floor((new Date().getTime() - dateInput.getTime()) / 1000);
571
+ if (timeDiff >= YEAR) {
572
+ return Math.floor(timeDiff / YEAR) + 'Y';
573
+ }
574
+ // if (timeDiff >= MONTH) {
575
+ // return Math.floor(timeDiff / MONTH) + 'M';
576
+ // }
577
+ if (timeDiff >= WEEK) {
578
+ return Math.floor(timeDiff / WEEK) + 'W';
579
+ }
580
+ if (timeDiff >= DAY) {
581
+ return Math.floor(timeDiff / DAY) + 'D';
582
+ }
583
+ if (timeDiff >= HOUR) {
584
+ return Math.floor(timeDiff / HOUR) + 'H';
585
+ }
586
+ if (timeDiff >= MINUTE) {
587
+ return Math.floor(timeDiff / MINUTE) + 'M';
588
+ }
589
+ return `${timeDiff}S`;
673
590
  };
591
+ function timePeriod(type) {
592
+ let endDate = new Date().getTime();
593
+ const today = new Date();
594
+ const month = today.getMonth();
595
+ const year = today.getFullYear();
596
+ let startDate = null;
597
+ switch (type) {
598
+ case '30d':
599
+ startDate = endDate - 60 * 60 * 24 * 30 * 1000;
600
+ break;
601
+ case '60d':
602
+ startDate = endDate - 60 * 60 * 24 * 60 * 1000;
603
+ break;
604
+ case '6m':
605
+ startDate = endDate - 60 * 60 * 24 * 30 * 6 * 1000;
606
+ break;
607
+ case 'this_month':
608
+ startDate = new Date(year, month, 1).getTime();
609
+ break;
610
+ case 'last_month':
611
+ startDate = new Date(year, month - 1, 1).getTime();
612
+ endDate = new Date(year, month, 0).getTime();
613
+ break;
614
+ case 'this_year':
615
+ startDate = new Date(year, 0, 1);
616
+ break;
617
+ case 'all_time':
618
+ default:
619
+ return null;
620
+ }
621
+ if (startDate === null && endDate === null) {
622
+ return null;
623
+ }
624
+ return {
625
+ start_time: new Date(startDate).toISOString(),
626
+ end_time: new Date(endDate).toISOString(),
627
+ };
628
+ }
674
629
 
675
- /**
676
- * Update a recipient's status block
677
- */
678
- const updateRecipient = async (endpoint, envelopeId, roleName, params) => endpoint.api //
679
- .put(`/envelopes/${envelopeId}/recipients/${roleName}`, params)
680
- .then((r) => r.data);
681
- /**
682
- * Submit an envelope (signing is finished). Note that all fields must be valid/completed for this to succeed.
683
- */
684
- const envelopeRecipientSubmit = (endpoint, envelopeId, roleName) => updateRecipient(endpoint, envelopeId, roleName, { action: 'submit' });
685
- /**
686
- * Decline to complete an envelope (signing will not terminated).
687
- */
688
- const envelopeRecipientDecline = (endpoint, envelopeId, roleName) => updateRecipient(endpoint, envelopeId, roleName, { action: 'decline' });
689
- /**
690
- * Claim / change ownership of an envelope. This is a special-case operation only available in certain workflows.
691
- */
692
- const envelopeRecipientChangeOwner = (endpoint, envelopeId, roleName, email, fullName) => updateRecipient(endpoint, envelopeId, roleName, { action: 'owner_update', email, full_name: fullName });
693
- /**
694
- * Agree to electronic signing.
695
- */
696
- const envelopeRecipientAgree = (endpoint, envelopeId, roleName, agreed) => updateRecipient(endpoint, envelopeId, roleName, { action: 'update', agreed });
697
- /**
698
- * Change a recipient's name.
699
- */
700
- const envelopeRecipientUpdateName = (endpoint, envelopeId, roleName, fullName) => updateRecipient(endpoint, envelopeId, roleName, { action: 'update', new_full_name: fullName });
701
- /**
702
- * Change a recipient's name.
703
- */
704
- const envelopeRecipientPrepare = (endpoint, envelopeId, roleName, recipients) => updateRecipient(endpoint, envelopeId, roleName, { action: 'prepare', recipients });
705
- /**
706
- * Get a signing token.
707
- */
708
- const getSignerToken = (endpoint, envelopeId, roleName) => endpoint.api //
709
- .get(`/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}/signer-token`)
710
- .then((r) => r.data);
711
- /**
712
- * Get an in-person signing link.
713
- */
714
- const getInPersonLink = (endpoint, envelopeId, roleName) => endpoint.api //
715
- .get(`/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}?in_person_link=true`)
716
- .then((r) => r.data);
717
- /**
718
- * Send a delegation request.
719
- */
720
- const sendDelegate = (endpoint, envelopeId, roleName) => endpoint.api //
721
- .post(`/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}/delegate`)
722
- .then((r) => r.data);
723
- /**
724
- * Resend a recipient's invitation.
725
- */
726
- const resendInvitation = (endpoint, envelopeId, roleName) => endpoint.api //
727
- .post(`/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}/resend_invitation`)
728
- .then((r) => r.data);
630
+ function getRTop(y, fieldHeight, iTextHeight, yRatio) {
631
+ return iTextHeight - (y + fieldHeight) * yRatio;
632
+ }
633
+ function getRLeft(x, ratio) {
634
+ return x * ratio;
635
+ }
636
+ function getRValue(y, ratio) {
637
+ return y * ratio;
638
+ }
639
+ function blobToBase64(image) {
640
+ const fileReader = new FileReader();
641
+ return new Promise((resolve, reject) => {
642
+ fileReader.onerror = () => {
643
+ reject(new DOMException('Problem reading blob.'));
644
+ };
645
+ fileReader.onload = () => {
646
+ resolve(fileReader.result);
647
+ };
648
+ fileReader.readAsDataURL(image);
649
+ });
650
+ }
651
+ function rescale(r, n) {
652
+ return r * n;
653
+ }
729
654
 
730
655
  /**
731
- * Enable automatic reminders. setup_time is the number of days after the envelope is sent that the first reminder
732
- * should be sent. interval_time is the number of days between reminders.
733
- */
734
- const createEnvelopeReminder = (endpoint, envelopeId, params) => endpoint.api //
735
- .post(`/envelopes/${envelopeId}/reminder/`, params)
736
- .then((r) => r.data);
737
- /**
738
- * Get the reminder configuration for an envelope.
739
- */
740
- const getEnvelopeReminder = (endpoint, envelopeId, reminderId) => endpoint.api //
741
- .get(`/envelopes/${envelopeId}/reminder/${reminderId}`)
742
- .then((r) => r.data);
743
- /**
744
- * Update the reminder configuration for an envelope.
745
- */
746
- const updateEnvelopeReminder = (endpoint, envelopeId, reminderId, params) => endpoint.api //
747
- .put(`/envelopes/${envelopeId}/reminder/${reminderId}`, params)
748
- .then((r) => r.data);
749
- /**
750
- * Delete the reminder configuration for an envelope.
656
+ * Given a File, extract the file's content as a base64 encoded data URL. The response will have a prefix that
657
+ * includes the MIME type of the file, e.g. "data:image/jpeg;base64,iVBORw0K......"
751
658
  */
752
- const deleteEnvelopeReminder = (endpoint, envelopeId, reminderId) => endpoint.api //
753
- .delete(`/envelopes/${envelopeId}/reminder/${reminderId}`)
754
- .then((r) => r.data);
755
-
659
+ const fileToDataUrl = (file) => new Promise((resolve, reject) => {
660
+ const reader = new FileReader();
661
+ reader.onload = () => resolve({
662
+ lastModified: file.lastModified,
663
+ size: file.size,
664
+ type: file.type,
665
+ name: file.name,
666
+ data: reader.result,
667
+ });
668
+ reader.onerror = reject;
669
+ if (file) {
670
+ reader.readAsDataURL(file);
671
+ }
672
+ else {
673
+ reject(new Error('Invalid file'));
674
+ }
675
+ });
756
676
  /**
757
- * Various helpers to identify available operations for an envelope by a user.
758
- *
759
- * @module
677
+ * Trigger a download dialog to save a blob as a file on disk.
760
678
  */
761
- /**
762
- * Check to see if the user owns the envelope.
763
- */
764
- const userIsEnvelopeOwner = (session, envelope) => envelope.profile_id === session?.profile_id;
765
- /**
766
- * Check to see if the user owns the envelope.
767
- */
768
- const userIsEnvelopeRecipient = (session, envelope) => envelope.profile_id === session?.profile_id;
769
- /**
770
- * Check to see if the envelope has pending actions.
771
- */
772
- const envelopeIsActive = (envelope) => envelope.status !== 'complete' && envelope.status !== 'declined' && envelope.status !== 'canceled';
773
- /**
774
- * Check to see if the envelope has been completed.
775
- */
776
- const envelopeIsComplete = (envelope) => envelope.status !== 'complete';
777
- /**
778
- * Check to see if the user owns the envelope.
779
- */
780
- const userCanCancelEnvelope = (session, envelope) => userIsEnvelopeOwner(session, envelope) &&
781
- envelope.status !== 'complete' &&
782
- envelope.status !== 'declined' &&
783
- envelope.status !== 'canceled';
784
- /**
785
- * Check to see if the user owns the envelope.
786
- */
787
- const userCanFinishEnvelope = (session, envelope) => userIsEnvelopeOwner(session, envelope) &&
788
- envelope.status !== 'complete' &&
789
- envelope.status !== 'declined' &&
790
- envelope.status !== 'canceled';
791
- /**
792
- * Returns true if the recipient has a pending action. Note that this does not necessarily mean the recipient can act (yet).
793
- */
794
- const recipientHasAction = (recipient) => !['submitted', 'canceled', 'declined'].includes(recipient.status);
795
- /**
796
- * Returns the recipients who still have a pending action. Note that not all of these recipients may be able to act (yet).
797
- */
798
- const getRecipientsWithActions = (envelope) => (envelope?.recipients || []).filter(recipientHasAction);
799
- /**
800
- * Returns true if the recipient can act.
801
- */
802
- const recipientCanAct = (recipient, recipientsWithActions) => recipient.sequence === recipientsWithActions?.[0]?.sequence;
803
- /**
804
- * Returns true if the user can act.
805
- */
806
- const userCanAct = (email, recipientsWithActions) => {
807
- const recipient = recipientsWithActions.find((r) => r.email === email);
808
- return recipient && recipient.sequence === recipientsWithActions?.[0]?.sequence;
809
- };
810
- /**
811
- * Returns true if the user can act.
812
- */
813
- const userCanSignNow = (session, envelope) => {
814
- if (!session) {
815
- return false;
816
- }
817
- const recipientsWithActions = getRecipientsWithActions(envelope);
818
- const myRecipient = recipientsWithActions.find((r) => r.profile_id === session?.profile_id || r.email === session?.email);
819
- return (myRecipient &&
820
- envelopeIsActive(envelope) &&
821
- userIsEnvelopeRecipient(session, envelope) &&
822
- recipientCanAct(myRecipient, recipientsWithActions));
823
- };
824
- const getNextRecipient = (envelope) => {
825
- const recipientsWithActions = getRecipientsWithActions(envelope);
826
- return recipientsWithActions?.[0];
827
- };
828
-
829
- /**
830
- * Create a signature block. In a typical signing workflow, the user is asked at the beginning of the process to "adopt"
831
- * a signature block to be used for all signature fields in the document. Thus, this is typically called one time to
832
- * create and store a signature block. Thereafter, the ID of the signature block may be re-used for each signature field
833
- * to be "stamped" by the user.
834
- */
835
- const createSignature = (endpoint, name, signature) => {
836
- const data = new FormData();
837
- data.append('signature', signature, name);
838
- return endpoint.api //
839
- .post(`/signatures`, data)
840
- .then((r) => r.data);
679
+ const downloadBlob = (blob, name = 'file.pdf') => {
680
+ const blobUrl = URL.createObjectURL(blob);
681
+ const link = document.createElement('a');
682
+ link.href = blobUrl;
683
+ link.download = name;
684
+ document.body.appendChild(link);
685
+ link.dispatchEvent(new MouseEvent('click', {
686
+ bubbles: true,
687
+ cancelable: true,
688
+ view: window,
689
+ }));
690
+ document.body.removeChild(link);
841
691
  };
842
- /**
843
- * Get the availbable signatures for a user.
844
- */
845
- const getSignatures = (endpoint) => endpoint.api //
846
- .get('/signatures')
847
- .then((r) => r.data);
848
- /**
849
- * Get a user's signature by ID.
850
- */
851
- const getSignature = (endpoint, signatureId) => endpoint.api //
852
- .get(`/signatures/${signatureId}`)
853
- .then((r) => r.data);
854
- /**
855
- * Delete a user's signature.
856
- */
857
- const deleteSignature = (endpoint, signatureId) => endpoint.api //
858
- .delete(`/signatures/${signatureId}`)
859
- .then((r) => r.data);
860
-
861
- /**
862
- * API keys are used to authenticate server-to-server calls. (API keys should **never** be used for client-to-server operations!)
863
- * To generate a key, either use the Verdocs admin interface and make note of the client_id and client_secret generated, or call
864
- * createKey as shown below. Then call {@link Users.Auth.authenticateApp} to obtain an access token using the provided ID and
865
- * secret. Note that server-to-server authentication requests return shorter-lived tokens, so it is important to check the `exp`
866
- * field and re-authenticate as needed for subsequent calls.
867
- *
868
- * API keys may be updated or rotated at any time. Regular rotation is recommended. Rotation will not expire or invalidate
869
- * existing server-to-server sessions, so it may be done at any time without disrupting your application.
870
- *
871
- * @module
872
- */
873
- /**
874
- * Get a list of keys for a given organization. The caller must have admin access to the organization.
875
- *
876
- * ```typescript
877
- * import {ApiKeys} from '@verdocs/js-sdk/Organizations';
878
- *
879
- * const keys = await ApiKeys.getKeys(ORGID);
880
- * ```
881
- */
882
- const getApiKeys = (endpoint, organizationId) => endpoint.api //
883
- .get(`/organizations/${organizationId}/api_key`)
884
- .then((r) => r.data);
885
- /**
886
- * Create an API key.
887
- *
888
- * ```typescript
889
- * import {ApiKeys} from '@verdocs/js-sdk/Organizations';
890
- *
891
- * await ApiKeys.createKey(ORGID, {name: NEWNAME});
892
- * ```
893
- */
894
- const createApiKey = (endpoint, organizationId, params) => endpoint.api //
895
- .post(`/organizations/${organizationId}/api_key`, params)
896
- .then((r) => r.data);
897
- /**
898
- * Rotate the secret for an API key. The caller must have admin access to the organization.
899
- *
900
- * ```typescript
901
- * import {ApiKeys} from '@verdocs/js-sdk/Organizations';
902
- *
903
- * const {client_secret: newSecret} = await ApiKeys.rotateKey(ORGID, CLIENTID);
904
- * ```
905
- */
906
- const rotateApiKey = (endpoint, organizationId, clientId) => endpoint.api //
907
- .put(`/organizations/${organizationId}/api_key/${clientId}/rotate`)
908
- .then((r) => r.data);
909
- /**
910
- * Update an API key to change its assigned Profile ID or Name.
911
- *
912
- * ```typescript
913
- * import {ApiKeys} from '@verdocs/js-sdk/Organizations';
914
- *
915
- * await ApiKeys.updateKey(ORGID, CLIENTID, {name: NEWNAME});
916
- * ```
917
- */
918
- const updateApiKey = (endpoint, organizationId, clientId, params) => endpoint.api //
919
- .patch(`/organizations/${organizationId}/api_key/${clientId}`, params)
920
- .then((r) => r.data);
921
- /**
922
- * Delete an API key.
923
- *
924
- * ```typescript
925
- * import {ApiKeys} from '@verdocs/js-sdk/Organizations';
926
- *
927
- * await ApiKeys.deleteKey(ORGID, CLIENTID);
928
- * ```
929
- */
930
- const deleteApiKey = (endpoint, organizationId, clientId) => endpoint.api //
931
- .delete(`/organizations/${organizationId}/api_key/${clientId}`)
932
- .then((r) => r.data);
933
-
934
- /**
935
- * Organizations may contain "Groups" of user profiles, called Members. Groups may have permissions assigned that
936
- * apply to all Members, making it easy to configure role-based access control (RBAC) within an Organization. Note
937
- * that permissions are **additive**. A user may be a member of more than one group, and may also have permissions
938
- * assigned directly. In that case, the user will have the combined set of all permissions inherited from all
939
- * sources.
940
- *
941
- * @module
942
- */
943
- /**
944
- * Get a list of groups for a given organization. The caller must have admin access to the organization.
945
- *
946
- * ```typescript
947
- * import {Groups} from '@verdocs/js-sdk/Organizations';
948
- *
949
- * const groups = await Groups.getGroups(ORGID);
950
- * ```
951
- */
952
- const getGroups = (endpoint, organizationId) => endpoint.api //
953
- .get(`/organizations/${organizationId}/groups`)
954
- .then((r) => r.data);
955
- /**
956
- * Get a single group by name. Returns a detail record.
957
- *
958
- * ```typescript
959
- * import {Groups} from '@verdocs/js-sdk/Organizations';
960
- *
961
- * const groups = await Groups.getGroups(ORGID);
962
- * ```
963
- */
964
- const getGroupByName = (endpoint, organizationId, name) => endpoint.api //
965
- .get(`/organizations/${organizationId}/groups`, { params: { name } })
966
- .then((r) => r.data);
967
- /**
968
- * Get the details for a group.
969
- *
970
- * ```typescript
971
- * import {Groups} from '@verdocs/js-sdk/Organizations';
972
- *
973
- * const groups = await Groups.getGroups(ORGID);
974
- * ```
975
- */
976
- const getGroup = (endpoint, organizationId, groupId) => endpoint.api //
977
- .get(`/organizations/${organizationId}/groups/${groupId}`)
978
- .then((r) => r.data);
979
- const getGroupMembers = (endpoint, organizationId, groupId) => endpoint.api //
980
- .get(`/organizations/${organizationId}/groups/${groupId}/members`)
981
- .then((r) => r.data);
982
- const addGroupMembers = (endpoint, organizationId, groupId, params) => endpoint.api //
983
- .post(`/organizations/${organizationId}/groups/${groupId}/members`, params)
984
- .then((r) => r.data);
985
- const deleteGroupMembers = (endpoint, organizationId, groupId, params) => endpoint.api //
986
- .put(`/organizations/${organizationId}/groups/${groupId}/delete_members`, params)
987
- .then((r) => r.data);
988
- const addGroupPermission = (endpoint, organizationId, groupId, permission) => endpoint.api //
989
- .post(`/organizations/${organizationId}/groups/${groupId}/permissions/${permission}`, {})
990
- .then((r) => r.data);
991
- const deleteGroupPermission = (endpoint, organizationId, groupId, permission) => endpoint.api //
992
- .delete(`/organizations/${organizationId}/groups/${groupId}/permissions/${permission}`)
993
- .then((r) => r.data);
994
692
 
995
- /**
996
- * An invitation represents an opportunity for a Member to join an Organization.
997
- *
998
- * @module
999
- */
1000
- const getOrganizationInvitations = (endpoint, organizationId) => endpoint.api //
1001
- .get(`/organizations/${organizationId}/invitation`)
1002
- .then((r) => r.data);
1003
- const createOrganizationInvitation = (endpoint, organizationId, params) => endpoint.api //
1004
- .post(`/organizations/${organizationId}/invitation`, params)
1005
- .then((r) => r.data);
1006
- const deleteOrganizationInvitation = (endpoint, organizationId, email) => endpoint.api //
1007
- .delete(`/organizations/${organizationId}/invitation/${email}`)
1008
- .then((r) => r.data);
1009
- const updateOrganizationInvitation = (endpoint, organizationId, email, params) => endpoint.api //
1010
- .patch(`/organizations/${organizationId}/invitation/${email}`, params)
1011
- .then((r) => r.data);
1012
- const resendOrganizationInvitation = (endpoint, organizationId, email) => endpoint.api //
1013
- .post(`/organizations/${organizationId}/invitation/${email}/resend`)
1014
- .then((r) => r.data);
1015
- const getOrganizationInvitation = (endpoint, organizationId, email, token) => endpoint.api //
1016
- .get(`/organizations/${organizationId}/invitation/${email}/accept/${token}`)
1017
- .then((r) => r.data);
1018
- const acceptOrganizationInvitation = (endpoint, organizationId, email, token) => endpoint.api //
1019
- .post(`/organizations/${organizationId}/invitation/${email}/accept/${token}`)
1020
- .then((r) => r.data);
1021
- const declineOrganizationInvitation = (endpoint, organizationId, email, token) => endpoint.api //
1022
- .post(`/organizations/${organizationId}/invitation/${email}/decline/${token}`)
1023
- .then((r) => r.data);
1024
- const claimNewUser = (endpoint, organizationId, email, token) => endpoint.api //
1025
- .put(`/organizations/${organizationId}/invitation/${email}/token/${token}/new_user`)
1026
- .then((r) => r.data);
1027
-
1028
- /**
1029
- * An Organization Member (aka Profile) is an individual user with access to an organization.
1030
- *
1031
- * @module
1032
- */
1033
- const getOrganizationMembers = (endpoint, organizationId) => endpoint.api //
1034
- .get(`/organizations/${organizationId}/profiles`)
1035
- .then((r) => r.data);
1036
- const deleteOrganizationMember = (endpoint, organizationId, profileId) => endpoint.api //
1037
- .delete(`/organizations/${organizationId}/profiles/${profileId}`)
1038
- .then((r) => r.data);
1039
- const addOrganizationMemberRole = (endpoint, organizationId, profileId, roleId) => endpoint.api //
1040
- .post(`/organizations/${organizationId}/profiles/${profileId}/role/${roleId}`)
1041
- .then((r) => r.data);
1042
- const deleteOrganizationMemberRole = (endpoint, organizationId, profileId, roleId) => endpoint.api //
1043
- .delete(`/organizations/${organizationId}/profiles/${profileId}/role/${roleId}`)
1044
- .then((r) => r.data);
1045
- const getOrganizationMemberPlans = (endpoint, organizationId, profileId) => endpoint.api //
1046
- .get(`/organizations/${organizationId}/profiles/${profileId}/plans`)
1047
- .then((r) => r.data);
1048
-
1049
- /**
1050
- * An Organization is the top level object for ownership for Members, Documents, and Templates.
1051
- *
1052
- * @module
1053
- */
1054
- /**
1055
- * Get a list of organizations the user has access to.
1056
- */
1057
- const getOrganizations = (endpoint) => endpoint.api //
1058
- .get('/organizations')
1059
- .then((r) => r.data);
1060
- /**
1061
- * Create an organization.
1062
- */
1063
- const createOrganization = (endpoint) => endpoint.api //
1064
- .post('/organizations')
1065
- .then((r) => r.data);
1066
- /**
1067
- * Delete an organization.
1068
- */
1069
- const deleteOrganization = (endpoint, organizationId) => endpoint.api //
1070
- .delete(`/organizations/${organizationId}`)
1071
- .then((r) => r.data);
1072
- /**
1073
- * Get an organization by ID.
1074
- */
1075
- const getOrganization = (endpoint, organizationId) => endpoint.api //
1076
- .get(`/organizations/${organizationId}`)
1077
- .then((r) => r.data);
1078
- /**
1079
- * Update an organization.
1080
- */
1081
- const updateOrganization = (endpoint, organizationId, params) => endpoint.api //
1082
- .patch(`/organizations/${organizationId}`, params)
1083
- .then((r) => r.data);
1084
-
1085
- const getWebhooks = (endpoint) => endpoint.api //
1086
- .get(`/v2/webhooks/organization`)
1087
- .then((r) => r.data);
1088
- const setWebhooks = (endpoint, params) => endpoint.api //
1089
- .post(`/v2/webhooks/organization`, params)
1090
- .then((r) => r.data);
1091
-
1092
- /**
1093
- * Confirm whether the user has all of the specified permissions.
1094
- */
1095
- const userHasPermissions = (session, permissions) => permissions.every((perm) => (session?.permissions || []).includes(perm));
1096
-
1097
- const canPerformTemplateAction = (session, action, template) => {
1098
- if (!template && !action.includes('create')) {
1099
- return { canPerform: false, message: 'Missing required template object' };
693
+ const Countries = [
694
+ { code: '+7 840', name: 'Abkhazia', value: '+7' },
695
+ { code: '+93', name: 'Afghanistan', value: '+93' },
696
+ { code: '+355', name: 'Albania', value: '+355' },
697
+ { code: '+213', name: 'Algeria', value: '+213' },
698
+ { code: '+1', name: 'American Samoa', value: '+1' },
699
+ { code: '+376', name: 'Andorra', value: '+376' },
700
+ { code: '+244', name: 'Angola', value: '+244' },
701
+ { code: '+1', name: 'Anguilla', value: '+1' },
702
+ { code: '+1', name: 'Antigua and Barbuda', value: '+1' },
703
+ { code: '+54', name: 'Argentina', value: '+54' },
704
+ { code: '+374', name: 'Armenia', value: '+374' },
705
+ { code: '+297', name: 'Aruba', value: '+297' },
706
+ { code: '+247', name: 'Ascension', value: '+247' },
707
+ { code: '+61', name: 'Australia', value: '+61' },
708
+ { code: '+672', name: 'Australian External Territories', value: '+672' },
709
+ { code: '+43', name: 'Austria', value: '+43' },
710
+ { code: '+994', name: 'Azerbaijan', value: '+994' },
711
+ { code: '+1', name: 'Bahamas', value: '+1' },
712
+ { code: '+973', name: 'Bahrain', value: '+973' },
713
+ { code: '+880', name: 'Bangladesh', value: '+880' },
714
+ { code: '+1', name: 'Barbados', value: '+1' },
715
+ { code: '+1', name: 'Barbuda', value: '+1' },
716
+ { code: '+375', name: 'Belarus', value: '+375' },
717
+ { code: '+32', name: 'Belgium', value: '+32' },
718
+ { code: '+501', name: 'Belize', value: '+501' },
719
+ { code: '+229', name: 'Benin', value: '+229' },
720
+ { code: '+1', name: 'Bermuda', value: '+1' },
721
+ { code: '+975', name: 'Bhutan', value: '+975' },
722
+ { code: '+591', name: 'Bolivia', value: '+591' },
723
+ { code: '+387', name: 'Bosnia and Herzegovina', value: '+387' },
724
+ { code: '+267', name: 'Botswana', value: '+267' },
725
+ { code: '+55', name: 'Brazil', value: '+55' },
726
+ { code: '+246', name: 'British Indian Ocean Territory', value: '+246' },
727
+ { code: '+1', name: 'British Virgin Islands', value: '+1' },
728
+ { code: '+673', name: 'Brunei', value: '+673' },
729
+ { code: '+359', name: 'Bulgaria', value: '+359' },
730
+ { code: '+226', name: 'Burkina Faso', value: '+226' },
731
+ { code: '+257', name: 'Burundi', value: '+257' },
732
+ { code: '+855', name: 'Cambodia', value: '+855' },
733
+ { code: '+237', name: 'Cameroon', value: '+237' },
734
+ { code: '+1', name: 'Canada', value: '+1' },
735
+ { code: '+238', name: 'Cape Verde', value: '+238' },
736
+ { code: '+1', name: 'Cayman Islands', value: '+1' },
737
+ { code: '+236', name: 'Central African Republic', value: '+236' },
738
+ { code: '+235', name: 'Chad', value: '+235' },
739
+ { code: '+56', name: 'Chile', value: '+56' },
740
+ { code: '+86', name: 'China', value: '+86' },
741
+ { code: '+61', name: 'Christmas Island', value: '+61' },
742
+ { code: '+61', name: 'Cocos-Keeling Islands', value: '+61' },
743
+ { code: '+57', name: 'Colombia', value: '+57' },
744
+ { code: '+269', name: 'Comoros', value: '+269' },
745
+ { code: '+242', name: 'Congo', value: '+242' },
746
+ { code: '+243', name: 'Congo, Dem. Rep. of (Zaire)', value: '+243' },
747
+ { code: '+682', name: 'Cook Islands', value: '+682' },
748
+ { code: '+506', name: 'Costa Rica', value: '+506' },
749
+ { code: '+385', name: 'Croatia', value: '+385' },
750
+ { code: '+53', name: 'Cuba', value: '+53' },
751
+ { code: '+599', name: 'Curacao', value: '+599' },
752
+ { code: '+537', name: 'Cyprus', value: '+537' },
753
+ { code: '+420', name: 'Czech Republic', value: '+420' },
754
+ { code: '+45', name: 'Denmark', value: '+45' },
755
+ { code: '+246', name: 'Diego Garcia', value: '+246' },
756
+ { code: '+253', name: 'Djibouti', value: '+253' },
757
+ { code: '+1', name: 'Dominica', value: '+1' },
758
+ { code: '+1', name: 'Dominican Republic', value: '+1' },
759
+ { code: '+670', name: 'East Timor', value: '+670' },
760
+ { code: '+56', name: 'Easter Island', value: '+56' },
761
+ { code: '+593', name: 'Ecuador', value: '+593' },
762
+ { code: '+20', name: 'Egypt', value: '+20' },
763
+ { code: '+503', name: 'El Salvador', value: '+503' },
764
+ { code: '+240', name: 'Equatorial Guinea', value: '+240' },
765
+ { code: '+291', name: 'Eritrea', value: '+291' },
766
+ { code: '+372', name: 'Estonia', value: '+372' },
767
+ { code: '+251', name: 'Ethiopia', value: '+251' },
768
+ { code: '+500', name: 'Falkland Islands', value: '+500' },
769
+ { code: '+298', name: 'Faroe Islands', value: '+298' },
770
+ { code: '+679', name: 'Fiji', value: '+679' },
771
+ { code: '+358', name: 'Finland', value: '+358' },
772
+ { code: '+33', name: 'France', value: '+33' },
773
+ { code: '+596', name: 'Martinique', value: '+596' },
774
+ { code: '+594', name: 'French Guiana', value: '+594' },
775
+ { code: '+689', name: 'French Polynesia', value: '+689' },
776
+ { code: '+241', name: 'Gabon', value: '+241' },
777
+ { code: '+220', name: 'Gambia', value: '+220' },
778
+ { code: '+995', name: 'Georgia', value: '+995' },
779
+ { code: '+49', name: 'Germany', value: '+49' },
780
+ { code: '+233', name: 'Ghana', value: '+233' },
781
+ { code: '+350', name: 'Gibraltar', value: '+350' },
782
+ { code: '+30', name: 'Greece', value: '+30' },
783
+ { code: '+299', name: 'Greenland', value: '+299' },
784
+ { code: '+1', name: 'Grenada', value: '+1' },
785
+ { code: '+590', name: 'Guadeloupe', value: '+590' },
786
+ { code: '+1', name: 'Guam', value: '+1' },
787
+ { code: '+502', name: 'Guatemala', value: '+502' },
788
+ { code: '+224', name: 'Guinea', value: '+224' },
789
+ { code: '+245', name: 'Guinea-Bissau', value: '+245' },
790
+ { code: '+595', name: 'Guyana', value: '+595' },
791
+ { code: '+509', name: 'Haiti', value: '+509' },
792
+ { code: '+504', name: 'Honduras', value: '+504' },
793
+ { code: '+852', name: 'Hong Kong SAR China', value: '+852' },
794
+ { code: '+36', name: 'Hungary', value: '+36' },
795
+ { code: '+354', name: 'Iceland', value: '+354' },
796
+ { code: '+91', name: 'India', value: '+91' },
797
+ { code: '+62', name: 'Indonesia', value: '+62' },
798
+ { code: '+98', name: 'Iran', value: '+98' },
799
+ { code: '+964', name: 'Iraq', value: '+964' },
800
+ { code: '+353', name: 'Ireland', value: '+353' },
801
+ { code: '+972', name: 'Israel', value: '+972' },
802
+ { code: '+39', name: 'Italy', value: '+39' },
803
+ { code: '+225', name: 'Ivory Coast', value: '+225' },
804
+ { code: '+1', name: 'Jamaica', value: '+1' },
805
+ { code: '+81', name: 'Japan', value: '+81' },
806
+ { code: '+962', name: 'Jordan', value: '+962' },
807
+ { code: '+77', name: 'Kazakhstan', value: '+7' },
808
+ { code: '+254', name: 'Kenya', value: '+254' },
809
+ { code: '+686', name: 'Kiribati', value: '+686' },
810
+ { code: '+965', name: 'Kuwait', value: '+965' },
811
+ { code: '+996', name: 'Kyrgyzstan', value: '+996' },
812
+ { code: '+856', name: 'Laos', value: '+856' },
813
+ { code: '+371', name: 'Latvia', value: '+371' },
814
+ { code: '+961', name: 'Lebanon', value: '+961' },
815
+ { code: '+266', name: 'Lesotho', value: '+266' },
816
+ { code: '+231', name: 'Liberia', value: '+231' },
817
+ { code: '+218', name: 'Libya', value: '+218' },
818
+ { code: '+423', name: 'Liechtenstein', value: '+423' },
819
+ { code: '+370', name: 'Lithuania', value: '+370' },
820
+ { code: '+352', name: 'Luxembourg', value: '+352' },
821
+ { code: '+853', name: 'Macau SAR China', value: '+853' },
822
+ { code: '+389', name: 'Macedonia', value: '+389' },
823
+ { code: '+261', name: 'Madagascar', value: '+261' },
824
+ { code: '+265', name: 'Malawi', value: '+265' },
825
+ { code: '+60', name: 'Malaysia', value: '+60' },
826
+ { code: '+960', name: 'Maldives', value: '+960' },
827
+ { code: '+223', name: 'Mali', value: '+223' },
828
+ { code: '+356', name: 'Malta', value: '+356' },
829
+ { code: '+692', name: 'Marshall Islands', value: '+692' },
830
+ { code: '+596', name: 'Martinique', value: '+596' },
831
+ { code: '+222', name: 'Mauritania', value: '+222' },
832
+ { code: '+230', name: 'Mauritius', value: '+230' },
833
+ { code: '+262', name: 'Mayotte or Réunion', value: '+262' },
834
+ { code: '+52', name: 'Mexico', value: '+52' },
835
+ { code: '+691', name: 'Micronesia', value: '+691' },
836
+ { code: '+1', name: 'Midway Island', value: '+1' },
837
+ { code: '+373', name: 'Moldova', value: '+373' },
838
+ { code: '+377', name: 'Monaco', value: '+377' },
839
+ { code: '+976', name: 'Mongolia', value: '+976' },
840
+ { code: '+382', name: 'Montenegro', value: '+382' },
841
+ { code: '+1', name: 'Montserrat', value: '+1' },
842
+ { code: '+212', name: 'Morocco', value: '+212' },
843
+ { code: '+95', name: 'Myanmar', value: '+95' },
844
+ { code: '+264', name: 'Namibia', value: '+264' },
845
+ { code: '+674', name: 'Nauru', value: '+674' },
846
+ { code: '+977', name: 'Nepal', value: '+977' },
847
+ { code: '+31', name: 'Netherlands', value: '+31' },
848
+ { code: '+599', name: 'Netherlands Antilles', value: '+599' },
849
+ { code: '+1', name: 'Nevis', value: '+1' },
850
+ { code: '+687', name: 'New Caledonia', value: '+687' },
851
+ { code: '+64', name: 'New Zealand', value: '+64' },
852
+ { code: '+505', name: 'Nicaragua', value: '+505' },
853
+ { code: '+227', name: 'Niger', value: '+227' },
854
+ { code: '+234', name: 'Nigeria', value: '+234' },
855
+ { code: '+683', name: 'Niue', value: '+683' },
856
+ { code: '+672', name: 'Norfolk Island', value: '+672' },
857
+ { code: '+850', name: 'North Korea', value: '+850' },
858
+ { code: '+1', name: 'Northern Mariana Islands', value: '+1' },
859
+ { code: '+47', name: 'Norway', value: '+47' },
860
+ { code: '+968', name: 'Oman', value: '+968' },
861
+ { code: '+92', name: 'Pakistan', value: '+92' },
862
+ { code: '+680', name: 'Palau', value: '+680' },
863
+ { code: '+970', name: 'Palestinian Territory', value: '+970' },
864
+ { code: '+507', name: 'Panama', value: '+507' },
865
+ { code: '+675', name: 'Papua New Guinea', value: '+675' },
866
+ { code: '+595', name: 'Paraguay', value: '+595' },
867
+ { code: '+51', name: 'Peru', value: '+51' },
868
+ { code: '+63', name: 'Philippines', value: '+63' },
869
+ { code: '+48', name: 'Poland', value: '+48' },
870
+ { code: '+351', name: 'Portugal', value: '+351' },
871
+ { code: '+1', name: 'Puerto Rico', value: '+1' },
872
+ { code: '+974', name: 'Qatar', value: '+974' },
873
+ { code: '+40', name: 'Romania', value: '+40' },
874
+ { code: '+7', name: 'Russia', value: '+7' },
875
+ { code: '+250', name: 'Rwanda', value: '+250' },
876
+ { code: '508', name: 'Saint Pierre and Miquelon', value: '508' },
877
+ { code: '+685', name: 'Samoa', value: '+685' },
878
+ { code: '+378', name: 'San Marino', value: '+378' },
879
+ { code: '+966', name: 'Saudi Arabia', value: '+966' },
880
+ { code: '+221', name: 'Senegal', value: '+221' },
881
+ { code: '+381', name: 'Serbia', value: '+381' },
882
+ { code: '+248', name: 'Seychelles', value: '+248' },
883
+ { code: '+232', name: 'Sierra Leone', value: '+232' },
884
+ { code: '+65', name: 'Singapore', value: '+65' },
885
+ { code: '+421', name: 'Slovakia', value: '+421' },
886
+ { code: '+386', name: 'Slovenia', value: '+386' },
887
+ { code: '+677', name: 'Solomon Islands', value: '+677' },
888
+ { code: '+27', name: 'South Africa', value: '+27' },
889
+ { code: '+500', name: 'South Georgia and the South Sandwich Islands', value: '+500' },
890
+ { code: '+82', name: 'South Korea', value: '+82' },
891
+ { code: '+34', name: 'Spain', value: '+34' },
892
+ { code: '+94', name: 'Sri Lanka', value: '+94' },
893
+ { code: '+249', name: 'Sudan', value: '+249' },
894
+ { code: '+597', name: 'Suriname', value: '+597' },
895
+ { code: '+268', name: 'Swaziland', value: '+268' },
896
+ { code: '+46', name: 'Sweden', value: '+46' },
897
+ { code: '+41', name: 'Switzerland', value: '+41' },
898
+ { code: '+963', name: 'Syria', value: '+963' },
899
+ { code: '+886', name: 'Taiwan', value: '+886' },
900
+ { code: '+992', name: 'Tajikistan', value: '+992' },
901
+ { code: '+255', name: 'Tanzania', value: '+255' },
902
+ { code: '+66', name: 'Thailand', value: '+66' },
903
+ { code: '+670', name: 'Timor Leste', value: '+670' },
904
+ { code: '+228', name: 'Togo', value: '+228' },
905
+ { code: '+690', name: 'Tokelau', value: '+690' },
906
+ { code: '+676', name: 'Tonga', value: '+676' },
907
+ { code: '+1', name: 'Trinidad and Tobago', value: '+1' },
908
+ { code: '+216', name: 'Tunisia', value: '+216' },
909
+ { code: '+90', name: 'Turkey', value: '+90' },
910
+ { code: '+993', name: 'Turkmenistan', value: '+993' },
911
+ { code: '+1', name: 'Turks and Caicos Islands', value: '+1' },
912
+ { code: '+688', name: 'Tuvalu', value: '+688' },
913
+ { code: '+1', name: 'U.S. Virgin Islands', value: '+1' },
914
+ { code: '+256', name: 'Uganda', value: '+256' },
915
+ { code: '+380', name: 'Ukraine', value: '+380' },
916
+ { code: '+971', name: 'United Arab Emirates', value: '+971' },
917
+ { code: '+44', name: 'United Kingdom', value: '+44' },
918
+ { code: '+1', name: 'United States', value: '+1' },
919
+ { code: '+598', name: 'Uruguay', value: '+598' },
920
+ { code: '+998', name: 'Uzbekistan', value: '+998' },
921
+ { code: '+678', name: 'Vanuatu', value: '+678' },
922
+ { code: '+58', name: 'Venezuela', value: '+58' },
923
+ { code: '+84', name: 'Vietnam', value: '+84' },
924
+ { code: '+1', name: 'Wake Island', value: '+1' },
925
+ { code: '+681', name: 'Wallis and Futuna', value: '+681' },
926
+ { code: '+967', name: 'Yemen', value: '+967' },
927
+ { code: '+260', name: 'Zambia', value: '+260' },
928
+ { code: '+255', name: 'Zanzibar', value: '+255' },
929
+ { code: '+263', name: 'Zimbabwe', value: '+263' },
930
+ ];
931
+ function getCountryByCode(code) {
932
+ const found = Countries.find((country) => country.code === code);
933
+ if (found)
934
+ return found;
935
+ if (isFrenchGuiana(code)) {
936
+ return { code: '+594', name: 'French Guiana', value: '+594' };
1100
937
  }
1101
- // We use BOGUS here to force the option-chain in things like template?.profile_id to NOT match profile?.profile_id because if both
1102
- // were undefined, they would actually match.
1103
- const profile_id = session?.profile_id || 'BOGUS';
1104
- const organization_id = session?.organization_id || 'BOGUS';
1105
- if (!profile_id) {
1106
- return { canPerform: false, message: 'Active session required' };
938
+ else if (isGuadeloupe(code)) {
939
+ return { code: '+590', name: 'Guadeloupe', value: '+590' };
1107
940
  }
1108
- const isCreator = template?.profile_id === profile_id;
1109
- const isSameOrg = template?.organization_id === organization_id;
1110
- const isPersonal = template?.is_personal ?? false;
1111
- const isPublic = template?.is_public ?? false;
1112
- const permissionsRequired = [];
1113
- switch (action) {
1114
- case 'create_personal':
1115
- permissionsRequired.push('template:creator:create:personal');
1116
- break;
1117
- case 'create_org':
1118
- permissionsRequired.push('template:creator:create:org');
941
+ else if (isMartinique(code)) {
942
+ return { code: '+596', name: 'Martinique', value: '+596' };
943
+ }
944
+ else if (isMayotte(code)) {
945
+ return { code: '+262', name: 'Mayotte or Réunion', value: '+262' };
946
+ }
947
+ return null;
948
+ }
949
+ function isFrenchGuiana(code) {
950
+ return '+594' === code.substring(0, 4);
951
+ }
952
+ function isGuadeloupe(code) {
953
+ return '+590' === code.substring(0, 4);
954
+ }
955
+ function isMartinique(code) {
956
+ return '+596' === code.substring(0, 4);
957
+ }
958
+ function isMayotte(code) {
959
+ return '+262' === code.substring(0, 4);
960
+ }
961
+ function getPlusOneCountry(code) {
962
+ let info = null;
963
+ switch (code.substring(0, 5)) {
964
+ case '+1684':
965
+ info = { code: '+1', name: 'American Samoa', value: '+1' };
1119
966
  break;
1120
- case 'create_public':
1121
- permissionsRequired.push('template:creator:create:public');
967
+ case '+1264':
968
+ info = { code: '+1', name: 'Anguilla', value: '+1' };
1122
969
  break;
1123
- case 'read':
1124
- if (!isCreator) {
1125
- if ((!isPersonal && isSameOrg) || !isPublic) {
1126
- permissionsRequired.push('template:member:read');
1127
- }
1128
- }
970
+ case '+1268':
971
+ info = { code: '+1', name: 'Antigua and Barbuda', value: '+1' };
1129
972
  break;
1130
- case 'write':
1131
- if (!isCreator) {
1132
- permissionsRequired.push('template:member:read');
1133
- permissionsRequired.push('template:member:write');
1134
- }
973
+ case '+1242':
974
+ info = { code: '+1', name: 'Bahamas', value: '+1' };
1135
975
  break;
1136
- case 'change_visibility_personal':
1137
- if (isCreator) {
1138
- permissionsRequired.push('template:creator:create:personal');
1139
- }
1140
- else {
1141
- permissionsRequired.push('template:member:visibility');
1142
- }
976
+ case '+1246':
977
+ info = { code: '+1', name: 'Barbados', value: '+1' };
1143
978
  break;
1144
- case 'change_visibility_org':
1145
- if (isCreator) {
1146
- permissionsRequired.push('template:creator:create:org');
1147
- }
1148
- else {
1149
- permissionsRequired.push('template:member:visibility');
1150
- }
979
+ case '+1441':
980
+ info = { code: '+1', name: 'Bermuda', value: '+1' };
1151
981
  break;
1152
- case 'change_visibility_public':
1153
- if (isCreator) {
1154
- permissionsRequired.push('template:creator:create:public');
1155
- permissionsRequired.push('template:creator:visibility');
1156
- }
1157
- else {
1158
- permissionsRequired.push('template:member:visibility');
1159
- }
982
+ case '+1284':
983
+ info = { code: '+1', name: 'British Virgin Islands', value: '+1' };
1160
984
  break;
1161
- case 'delete':
1162
- if (isCreator) {
1163
- permissionsRequired.push('template:creator:delete');
1164
- }
1165
- else {
1166
- permissionsRequired.push('template:member:delete');
1167
- }
985
+ case '+1':
986
+ info = { code: '+1', name: '', value: '+1' };
1168
987
  break;
1169
- default:
1170
- return { canPerform: false, message: 'Action is not defined' };
1171
- }
1172
- if (hasRequiredPermissions(session, permissionsRequired)) {
1173
- return { canPerform: true, message: '' };
1174
988
  }
1175
- return { canPerform: false, message: `Insufficient access to perform '${action}'. Needed permissions: ${permissionsRequired.toString()}` };
1176
- };
1177
- const hasRequiredPermissions = (session, permissions) => permissions.every((perm) => (session?.permissions || []).includes(perm));
989
+ return info;
990
+ }
991
+ function isCanada(code) {
992
+ const canadianAreaCodes = [
993
+ '403',
994
+ '587',
995
+ '780',
996
+ '825',
997
+ '604',
998
+ '250',
999
+ '778',
1000
+ '236',
1001
+ '204',
1002
+ '431',
1003
+ '506',
1004
+ '709',
1005
+ '867',
1006
+ '782',
1007
+ '902',
1008
+ '867',
1009
+ '548',
1010
+ '705',
1011
+ '365',
1012
+ '613',
1013
+ '807',
1014
+ '226',
1015
+ '289',
1016
+ '437',
1017
+ '519',
1018
+ '647',
1019
+ '905',
1020
+ '249',
1021
+ '343',
1022
+ '416',
1023
+ '902',
1024
+ '782',
1025
+ '450',
1026
+ '418',
1027
+ '579',
1028
+ '873',
1029
+ '367',
1030
+ '514',
1031
+ '581',
1032
+ '819',
1033
+ '438',
1034
+ '639',
1035
+ '306',
1036
+ '867',
1037
+ ];
1038
+ const areaCode = code.substring(0, 5);
1039
+ return canadianAreaCodes.findIndex((x) => '+1' + x === areaCode) > -1;
1040
+ }
1041
+ function isAmericanSamoa(code) {
1042
+ return code.substring(0, 5) === '+1684';
1043
+ }
1044
+ function isDominicanRepublic(code) {
1045
+ return '+1809' === code.substring(0, 5) || '+1829' === code.substring(0, 5) || '+1849' === code.substring(0, 5);
1046
+ }
1047
+ function isPuertoRico(code) {
1048
+ return code.substring(0, 5) === '+' || code.substring(0, 5) === '+';
1049
+ }
1050
+ // need to finish
1051
+ function getMatchingCountry(code, substrings) {
1052
+ const toMatch = code.substring(0, substrings);
1053
+ return Countries.filter((c) => c.code === toMatch).length;
1054
+ }
1055
+ // const e164Regex = new RegExp(/\+[1-9]\d{6,14}/g);
1056
+ // export function simpleE164Validator(code: string) {
1057
+ // return (code !== null && code.length < 16 && code.length > 6 && e164Regex.test(code)) || code === '' || code === null;
1058
+ // }
1178
1059
 
1179
1060
  /**
1180
- * Add a field to a template.
1181
- */
1182
- const createField = (endpoint, templateId, params) => endpoint.api //
1183
- .post(`/templates/${templateId}/fields`, params)
1184
- .then((r) => r.data);
1185
- /**
1186
- * Update a template field.
1061
+ * Capitalize the first letter of a string.
1187
1062
  */
1188
- const updateField = (endpoint, templateId, fieldName, params) => endpoint.api //
1189
- .put(`/templates/${templateId}/fields/${fieldName}`, params)
1190
- .then((r) => r.data);
1063
+ const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
1191
1064
  /**
1192
- * REmove a field from a template.
1065
+ * Convert a phone-number-like string to E164 format.
1066
+ * @see https://46elks.com/kb/e164
1193
1067
  */
1194
- const deleteField = (endpoint, templateId, fieldName) => endpoint.api //
1195
- .delete(`/templates/${templateId}/fields/${fieldName}`)
1196
- .then((r) => r.data);
1068
+ const convertToE164 = (input) => {
1069
+ // "(212) 555-1212" => +12125551212
1070
+ // "+46766861004" => "+46766861004"
1071
+ // "212-555-1212" => +12125551212
1072
+ // "212.555.1212" => +12125551212
1073
+ // "212 555 1212" => +12125551212
1074
+ let temp = (input || '').trim();
1075
+ // If we are already prefixed, assume the user did it deliberately and attempt to use what they entered. We also short-circuit blanks.
1076
+ if (!temp || temp.startsWith('+')) {
1077
+ return temp;
1078
+ }
1079
+ // Remove any spaces, parenthesis or other punctuation.
1080
+ temp = temp.replace(/[^0-9]/g, '');
1081
+ // If the number begins with a zero, remove the leading zero. Do not combine this with the previous step because it needs to be removed
1082
+ // whether it's the actual first character e.g. `0(5)` or just the first digit e.g. `(05`.
1083
+ temp = temp.replace(/^0/g, '');
1084
+ // Prepend the country code and +. We're assuming US in this case given the target demographic. Users in other countries would/should be
1085
+ // already entering a prefix so they'd shortcut out of this routine via the + prefix check.
1086
+ return `+1${temp}`;
1087
+ };
1197
1088
 
1198
1089
  /**
1199
- * Enable automatic reminders. setup_time is the number of days after the envelope is sent that the first reminder
1200
- * should be sent. interval_time is the number of days between reminders.
1090
+ * Create an array containing a sequence of integers, e.g. [START, START+1, START+2, ...] This is frequently useful
1091
+ * in rendering operations when there is no source array to .map() across.
1201
1092
  */
1202
- const createTemplateReminder = (endpoint, templateId, params) => endpoint.api //
1203
- .post(`/templates/${templateId}/reminder/`, params)
1204
- .then((r) => r.data);
1093
+ const integerSequence = (start, count) => Array(count)
1094
+ .fill(1)
1095
+ .map((_, index) => index + start);
1205
1096
  /**
1206
- * Get the reminder configuration for a template.
1097
+ * Format a profile's full name
1207
1098
  */
1208
- const getTemplateReminder = (endpoint, templateId, reminderId) => endpoint.api //
1209
- .get(`/templates/${templateId}/reminder/${reminderId}`)
1210
- .then((r) => r.data);
1099
+ const formatFullName = (profile) => profile ? `${capitalize(profile.first_name)} ${capitalize(profile.last_name)}` : 'Invalid User';
1211
1100
  /**
1212
- * Update the reminder configuration for a template.
1101
+ * Format a profile's initials
1213
1102
  */
1214
- const updateTemplateReminder = (endpoint, templateId, reminderId, params) => endpoint.api //
1215
- .put(`/templates/${templateId}/reminder/${reminderId}`, params)
1216
- .then((r) => r.data);
1103
+ const formatInitials = (profile) => profile ? `${capitalize(profile.first_name).charAt(0)} ${capitalize(profile.last_name).charAt(0)}` : '--';
1217
1104
  /**
1218
- * Delete the reminder configuration for a template.
1105
+ * Generate suggested initials for a full name, e.g. "John Doe" will yield "JD".
1219
1106
  */
1220
- const deleteTemplateReminder = (endpoint, templateId, reminderId) => endpoint.api //
1221
- .delete(`/templates/${templateId}/reminder/${reminderId}`)
1222
- .then((r) => r.data);
1107
+ const fullNameToInitials = (name) => name
1108
+ .split(' ')
1109
+ .map((word) => word[0])
1110
+ .join('');
1223
1111
 
1224
1112
  /**
1225
- * A "role" is an individual participant in a signing flow, such as a signer or CC contact. Roles are identified by
1226
- * their names, which must be unique (e.g. 'Recipient 1'). Template fields are assigned to roles for signing operations,
1227
- * so you may have 'Recipient 1 Signature 1' and so forth.
1113
+ * Create an envelope
1228
1114
  *
1229
- * @module
1230
- */
1231
- const createTemplateRole = (endpoint, templateId, params) => endpoint.api //
1232
- .post(`/templates/${templateId}/roles`, params)
1233
- .then((r) => r.data);
1234
- const getTemplateRoles = (endpoint, templateId) => endpoint.api //
1235
- .get(`/templates/${templateId}/roles`)
1236
- .then((r) => r.data);
1237
- const getTemplateRole = (endpoint, templateId, roleName) => endpoint.api //
1238
- .get(`/templates/${templateId}/roles/${roleName}`)
1239
- .then((r) => r.data);
1240
- const updateTemplateRole = (endpoint, templateId, roleName, params) => endpoint.api //
1241
- .put(`/templates/${templateId}/roles/${roleName}`, params)
1242
- .then((r) => r.data);
1243
- const deleteTemplateRole = (endpoint, templateId, roleName) => endpoint.api //
1244
- .delete(`/templates/${templateId}/roles/${roleName}`)
1245
- .then((r) => r.data);
1246
- const getTemplateRoleFields = (endpoint, templateId, roleName) => endpoint.api //
1247
- .get(`/templates/${templateId}/roles/${roleName}/fields`)
1248
- .then((r) => r.data);
1249
-
1250
- /**
1251
- * Get the template stars for a template.
1115
+ * ```typescript
1116
+ * import {Envelopes, ICreateEnvelopeRole, ICreateEnvelopeRequest} from '@verdocs/js-sdk/Envelopes';
1117
+ *
1118
+ * const role1: ICreateEnvelopeRole = {
1119
+ * type: 'signer',
1120
+ * name: 'Seller',
1121
+ * full_name: 'Paige Turner',
1122
+ * email: 'paige.turner@nomail.com',
1123
+ * phone: '',
1124
+ * sequence: 1,
1125
+ * delegator: false,
1126
+ * message: '',
1127
+ * };
1128
+ *
1129
+ * const role2: ICreateEnvelopeRole = {
1130
+ * type: 'signer',
1131
+ * name: 'Buyer',
1132
+ * full_name: 'Will Power',
1133
+ * email: 'will.power@nomail.com',
1134
+ * phone: '',
1135
+ * sequence: 2,
1136
+ * delegator: false,
1137
+ * message: '',
1138
+ * };
1139
+ *
1140
+ * const request: ICreateEnvelopeRequest = {template_id: 'd2338742-f3a1-465b-8592-806587413cc1', name: 'Bill of Sale', roles: [role1, role2]};
1141
+ * const {id, recipients} = await Envelopes.createEnvelope(VerdocsEndpoint.getDefault(), request);
1142
+ * ```
1252
1143
  */
1253
- const getStars = (endpoint, templateId) => endpoint.api //
1254
- .get(`/templates/${templateId}/stars`)
1144
+ const createEnvelope = async (endpoint, request) => endpoint.api //
1145
+ .post('/envelopes', request)
1255
1146
  .then((r) => r.data);
1256
1147
  /**
1257
- * Toggle the template star for a template.
1148
+ * Get a summary of currently active envelopes.
1149
+ *
1150
+ * ```typescript
1151
+ * import {Envelopes} from '@verdocs/js-sdk/Envelopes';
1152
+ *
1153
+ * const {action_required, completed, waiting_on_others} = await Envelopes.getSummary(VerdocsEndpoint.getDefault());
1154
+ * ```
1258
1155
  */
1259
- const toggleStar = (endpoint, templateId) => endpoint.api //
1260
- .post(`/templates/${templateId}/stars/toggle`)
1156
+ const getEnvelopesSummary = async (endpoint, page) => endpoint.api //
1157
+ .post('/envelopes/summary', { page })
1261
1158
  .then((r) => r.data);
1262
-
1263
1159
  /**
1264
- * A Tag is a user-specified label applied to a template. Tags help users organize and find Templates.
1265
- * recipients. Every Organization has a set of tags "owned" by that Organization and only visible inside it.
1266
- * Verdocs also provides a set of system-wide "featured" tags available to all Organizations.
1160
+ * Search for envelopes matching various criteria.
1267
1161
  *
1268
- * @module
1162
+ * ```typescript
1163
+ * import {Envelopes} from '@verdocs/js-sdk/Envelopes';
1164
+ *
1165
+ * const {result, page, total} = await Envelopes.search(VerdocsEndpoint.getDefault(), { ... });
1166
+ * ```
1269
1167
  */
1168
+ const searchEnvelopes = async (endpoint, params) => endpoint.api //
1169
+ .post('/envelopes/search', params)
1170
+ .then((r) => r.data);
1270
1171
  /**
1271
- * Apply a tag to a template.
1172
+ * Get a signing session for an Envelope.
1272
1173
  */
1273
- const addTemplateTag = (endpoint, templateId, params) => endpoint.api //
1274
- .post(`/templates/${templateId}/tags/`, params)
1275
- .then((r) => r.data);
1174
+ const getSigningSession = async (endpoint, params) => {
1175
+ window.console.log('[JS_SDK] getSigningSession', params, endpoint.api);
1176
+ return endpoint.api //
1177
+ .get(`/envelopes/${params.envelopeId}/recipients/${encodeURIComponent(params.roleId)}/invitation/${params.inviteCode}`)
1178
+ .then((r) => {
1179
+ // Avoiding a jsonwebtoken dependency here - we don't actually need the whole library
1180
+ const signerToken = r.headers?.signer_token || '';
1181
+ const session = decodeAccessTokenBody(signerToken);
1182
+ endpoint.setToken(signerToken);
1183
+ return { recipient: r.data, session, signerToken };
1184
+ });
1185
+ };
1276
1186
  /**
1277
- * Get all tags for a template.
1187
+ * Get the list of recipients for an Envelope.
1278
1188
  */
1279
- const getTemplateTags = (endpoint, templateId) => endpoint.api //
1280
- .get(`/templates/${templateId}/tags/`)
1189
+ const getEnvelopeRecipients = async (endpoint, envelopeId) => endpoint.api //
1190
+ .get(`/envelopes/${envelopeId}/recipients`)
1281
1191
  .then((r) => r.data);
1282
1192
  /**
1283
- * Remove a tag from a template.
1193
+ * Get all metadata for an Envelope.
1284
1194
  */
1285
- const deleteTemplateTag = (endpoint, templateId, tagName) => endpoint.api //
1286
- .post(`/templates/${templateId}/tags/${tagName}`)
1195
+ const getEnvelope = async (endpoint, envelopeId) => endpoint.api //
1196
+ .get(`/envelopes/${envelopeId}`)
1287
1197
  .then((r) => r.data);
1288
1198
  /**
1289
- * Create an Organization-wide tag.
1199
+ * Get an Envelope Document
1290
1200
  */
1291
- const createTag = (endpoint, name) => endpoint.api //
1292
- .post('/tags', { tag_name: name })
1201
+ const getEnvelopeDocument = async (endpoint, envelopeId, documentId) => endpoint.api //
1202
+ .get(`/envelopes/${envelopeId}/envelope_documents/${documentId}`)
1293
1203
  .then((r) => r.data);
1294
1204
  /**
1295
- * Get an Organization-wide tag.
1205
+ * Get a pre-signed download link for an Envelope Document. This link expires quickly, so it should
1206
+ * be accessed immediately and never shared. Content-Disposition will be set to "download".
1296
1207
  */
1297
- const getTag = (endpoint, name) => endpoint.api //
1298
- .get(`/tags/${name}`)
1208
+ const getDocumentDownloadLink = async (endpoint, envelopeId, documentId) => endpoint.api //
1209
+ .get(`/envelopes/${envelopeId}/envelope_documents/${documentId}?download=true`)
1299
1210
  .then((r) => r.data);
1300
1211
  /**
1301
- * Get all tags available for use by an Organization.
1212
+ * Get a pre-signed preview link for an Envelope Document. This link expires quickly, so it should
1213
+ * be accessed immediately and never shared. Content-Disposition will be set to "inline".
1302
1214
  */
1303
- const getAllTags = (endpoint) => endpoint.api //
1304
- .get('/tags')
1215
+ const getDocumentPreviewLink = async (endpoint, envelopeId, documentId) => endpoint.api //
1216
+ .get(`/envelopes/${envelopeId}/envelope_documents/${documentId}?preview=true`)
1305
1217
  .then((r) => r.data);
1306
-
1307
1218
  /**
1308
- * A Template defines how a Verdocs signing flow will be performed, including attachments, signing fields, and
1309
- * recipients.
1310
- *
1311
- * @module
1219
+ * Cancel an Envelope.
1312
1220
  */
1221
+ const cancelEnvelope = async (endpoint, envelopeId) => endpoint.api //
1222
+ .put(`/envelopes/${envelopeId}`, { action: 'cancel' })
1223
+ .then((r) => r.data);
1313
1224
  /**
1314
- * Get all templates accessible by the caller, with optional filters.
1315
- *
1316
- * ```typescript
1317
- * import {Templates} from '@verdocs/js-sdk/Templates';
1318
- *
1319
- * await Templates.getTemplates((VerdocsEndpoint.getDefault());
1320
- * await Templates.getTemplates((VerdocsEndpoint.getDefault(), { is_starred: true });
1321
- * await Templates.getTemplates((VerdocsEndpoint.getDefault(), { is_creator: true });
1322
- * await Templates.getTemplates((VerdocsEndpoint.getDefault(), { is_organization: true });
1323
- * ```
1225
+ * Get (binary download) a file attached to an Envelope. It is important to use this method
1226
+ * rather than a direct A HREF or similar link to set the authorization headers for the
1227
+ * request.
1324
1228
  */
1325
- const getTemplates = (endpoint, params) => endpoint.api //
1326
- .post('/templates', { params })
1229
+ const getEnvelopeFile = async (endpoint, envelopeId, documentId) => endpoint.api //
1230
+ .get(`/envelopes/${envelopeId}/envelope_documents/${documentId}?file=true`, { responseType: 'blob' })
1327
1231
  .then((r) => r.data);
1328
- // export interface IListTemplatesParams {
1329
- // name?: string;
1330
- // sharing?: 'all' | 'personal' | 'shared' | 'public';
1331
- // starred?: 'all' | 'starred' | 'unstarred';
1332
- // sort?: 'name' | 'created_at' | 'updated_at' | 'last_used_at' | 'counter' | 'star_counter';
1333
- // direction?: 'asc' | 'desc';
1334
- // page?: number;
1335
- // rows?: number;
1336
- // }
1337
1232
  /**
1338
- * Lists all templates accessible by the caller, with optional filters.
1339
- *
1340
- * ```typescript
1341
- * import {Templates} from '@verdocs/js-sdk/Templates';
1342
- *
1343
- * await Templates.listTemplates((VerdocsEndpoint.getDefault(), { sharing: 'personal', sort: 'last_used_at' });
1344
- * ```
1233
+ * Update a Document field. Typically called during the signing process as a Recipient fills in fields.
1345
1234
  */
1346
- // export const listTemplates = (endpoint: VerdocsEndpoint, params?: IListTemplatesParams) =>
1347
- // endpoint.api //
1348
- // .post<ITemplateSummaries>('/templates/list', params, {baseURL: endpoint.getBaseURLv2()})
1349
- // .then((r) => r.data);
1235
+ const updateEnvelopeField = async (endpoint, envelopeId, fieldName, value) => endpoint.api //
1236
+ .put(`/envelopes/${envelopeId}/fields/${fieldName}`, value)
1237
+ .then((r) => r.data);
1350
1238
  /**
1351
- * Get one template by its ID.
1352
- *
1353
- * ```typescript
1354
- * import {Templates} from '@verdocs/js-sdk/Templates';
1355
- *
1356
- * const template = await Templates.getTemplate((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9');
1357
- * ```
1239
+ * Update a Document signature field. Signature fields are ID-driven. Call `Document.createSignature()` first to create a
1240
+ * signature for a Recipient, then call `Documents.updateDocumentFieldSignature()` to attach it to a field.
1358
1241
  */
1359
- const getTemplate = (endpoint, templateId) => endpoint.api //
1360
- .get(`/templates/${templateId}`)
1242
+ const updateEnvelopeFieldSignature = async (endpoint, envelopeId, fieldName, signatureId) => endpoint.api //
1243
+ .put(`/envelopes/${envelopeId}/fields/${fieldName}/signature/${signatureId}`)
1361
1244
  .then((r) => r.data);
1362
1245
  /**
1363
- * Get owner information for a template.
1364
- *
1365
- * ```typescript
1366
- * import {Templates} from '@verdocs/js-sdk/Templates';
1367
- *
1368
- * const template = await Templates.getTemplateOwnerInfo((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9');
1369
- * ```
1246
+ * Update a Document signature field. Signature fields are ID-driven. Call `Document.createSignature()` first to create a
1247
+ * signature for a Recipient, then call `Documents.updateDocumentFieldSignature()` to attach it to a field.
1370
1248
  */
1371
- const getTemplateOwnerInfo = (endpoint, templateId) => endpoint.api //
1372
- .get(`/templates/${templateId}`)
1249
+ const updateEnvelopeFieldInitials = async (endpoint, envelopeId, fieldName, initialId) => endpoint.api //
1250
+ .put(`/envelopes/${envelopeId}/fields/${fieldName}/initial/${initialId}`)
1373
1251
  .then((r) => r.data);
1374
- const ALLOWED_CREATE_FIELDS = [
1375
- 'name',
1376
- 'is_personal',
1377
- 'is_public',
1378
- 'sender',
1379
- 'description',
1380
- 'roles',
1381
- 'fields',
1382
- ];
1383
1252
  /**
1384
- * Create a template.
1385
- *
1386
- * ```typescript
1387
- * import {Templates} from '@verdocs/js-sdk/Templates';
1388
- *
1389
- * const newTemplate = await Templates.createTemplate((VerdocsEndpoint.getDefault(), {...});
1390
- * ```
1253
+ * Upload an attachment.
1391
1254
  */
1392
- const createTemplate = (endpoint, params, onUploadProgress) => {
1393
- const options = {
1255
+ const uploadEnvelopeFieldAttachment = async (endpoint, envelopeId, fieldName, file, onUploadProgress) => {
1256
+ const formData = new FormData();
1257
+ formData.append('document', file, file.name);
1258
+ return endpoint.api //
1259
+ .put(`/envelopes/${envelopeId}/fields/${fieldName}`, formData, {
1394
1260
  timeout: 120000,
1395
1261
  onUploadProgress: (event) => {
1396
1262
  const total = event.total || 1;
1397
1263
  const loaded = event.loaded || 0;
1398
1264
  onUploadProgress?.(Math.floor((loaded * 100) / (total || 1)), loaded, total || 1);
1399
1265
  },
1400
- };
1401
- if (params.documents && params.documents[0] instanceof File) {
1402
- if (params.documents.length > 10) {
1403
- throw new Error('createTemplate() has a maximum of 10 documents that can be attached.');
1404
- }
1405
- const formData = new FormData();
1406
- ALLOWED_CREATE_FIELDS.forEach((allowedKey) => {
1407
- if (params[allowedKey] !== undefined) {
1408
- formData.append(allowedKey, params[allowedKey]);
1409
- }
1410
- });
1411
- params.documents.forEach((file) => {
1412
- formData.append('documents', file, file.name);
1413
- });
1414
- return endpoint.api.post('/templates', formData, options).then((r) => r.data);
1415
- }
1416
- else {
1417
- return endpoint.api.post('/templates', params, options).then((r) => r.data);
1418
- }
1266
+ })
1267
+ .then((r) => r.data);
1419
1268
  };
1420
1269
  /**
1421
- * Create a template.
1422
- *
1423
- * ```typescript
1424
- * import {Templates} from '@verdocs/js-sdk/Templates';
1425
- *
1426
- * const newTemplate = await Templates.createTemplatev2((VerdocsEndpoint.getDefault(), {...});
1427
- * ```
1270
+ * Delete an attachment.
1428
1271
  */
1429
- const createTemplatev2 = (endpoint, params, onUploadProgress) => {
1430
- const options = {
1272
+ const deleteEnvelopeFieldAttachment = async (endpoint, envelopeId, fieldName, file, onUploadProgress) => {
1273
+ const formData = new FormData();
1274
+ // Omitting file is the trigger here
1275
+ return endpoint.api //
1276
+ .put(`/envelopes/${envelopeId}/fields/${fieldName}`, formData, {
1431
1277
  timeout: 120000,
1432
1278
  onUploadProgress: (event) => {
1433
1279
  const total = event.total || 1;
1434
1280
  const loaded = event.loaded || 0;
1435
1281
  onUploadProgress?.(Math.floor((loaded * 100) / (total || 1)), loaded, total || 1);
1436
1282
  },
1437
- };
1438
- if (params.documents && params.documents[0] instanceof File) {
1439
- const formData = new FormData();
1440
- ALLOWED_CREATE_FIELDS.forEach((allowedKey) => {
1441
- if (params[allowedKey] !== undefined) {
1442
- formData.append(allowedKey, params[allowedKey]);
1443
- }
1444
- });
1445
- params.documents.forEach((file) => {
1446
- formData.append('documents', file, file.name);
1447
- });
1448
- return endpoint.api.post('/v2/templates', formData, options).then((r) => r.data);
1449
- }
1450
- else {
1451
- return endpoint.api.post('/v2/templates', params, options).then((r) => r.data);
1283
+ })
1284
+ .then((r) => r.data);
1285
+ };
1286
+ /**
1287
+ * Get the attached file for an attachment field (if any)
1288
+ */
1289
+ const getFieldAttachment = async (endpoint, envelopeId, fieldName) => endpoint.api //
1290
+ .get(`/envelopes/${envelopeId}/fields/${fieldName}/document`, { responseType: 'blob' })
1291
+ .then((r) => r.data);
1292
+ /**
1293
+ * Get a display URI for a given page in a file attached to an envelope document. These pages are rendered server-side
1294
+ * into PNG resources suitable for display in IMG tags although they may be used elsewhere. Note that these are intended
1295
+ * for DISPLAY ONLY, are not legally binding documents, and do not contain any encoded metadata from participants.
1296
+ */
1297
+ const getEnvelopeDocumentPageDisplayUri = async (endpoint, envelopeId, documentId, page, type = 'original') => endpoint.api
1298
+ .get(`/envelopes/${envelopeId}/envelope_documents/${documentId}/pages/${page}/image?type=${type}`, { timeout: 20000 })
1299
+ .then((r) => r.data);
1300
+ const cachedEnvelopes = {};
1301
+ /**
1302
+ * Wrapper for `getEnvelope()` that limits queries to one every 2 seconds per template ID.
1303
+ * This is intended for use in component hierarchies that all rely on the same template
1304
+ * to avoid unnecessary repeat server calls.
1305
+ */
1306
+ const throttledGetEnvelope = (endpoint, envelopeId) => {
1307
+ if (cachedEnvelopes[envelopeId] && cachedEnvelopes[envelopeId].loaded + 2000 < new Date().getTime()) {
1308
+ return cachedEnvelopes[envelopeId].envelope;
1452
1309
  }
1310
+ return getEnvelope(endpoint, envelopeId).then((envelope) => {
1311
+ cachedEnvelopes[envelopeId] = { loaded: new Date().getTime(), envelope };
1312
+ return envelope;
1313
+ });
1314
+ };
1315
+ /**a
1316
+ * Lists all envelopes accessible by the caller, with optional filters.
1317
+ *
1318
+ * ```typescript
1319
+ * import {Envelopes} from '@verdocs/js-sdk/Envelopes';
1320
+ *
1321
+ * const {totals, envelopes} = await Envelopes.listEnvelopes((VerdocsEndpoint.getDefault(), { q: 'test', sort: 'created_at' });
1322
+ * ```
1323
+ */
1324
+ const listEnvelopes = (endpoint, params) => endpoint.api //
1325
+ .post('/envelopes/list', params)
1326
+ .then((r) => r.data);
1327
+ /**
1328
+ * Get all of the envelopes that were sent using a given template.
1329
+ * NOTE: This endpoint will be retired soon. Its response is not paginated and it is typically used only to retrieve
1330
+ * "submitted data" for a template. A new endpoint will be introduced to provide this function more directly.
1331
+ * @deprecated
1332
+ */
1333
+ const getEnvelopesByTemplateId = async (endpoint, templateId) => endpoint.api //
1334
+ .get(`/envelopes?template_id=${templateId}`)
1335
+ .then((r) => r.data);
1336
+
1337
+ /**
1338
+ * Create an initials block. In a typical signing workflow, the user is asked at the beginning of the process to "adopt"
1339
+ * an initials block to be used for all initials fields in the document. Thus, this is typically called one time to
1340
+ * create and store an initials block. Thereafter, the ID of the initials block may be re-used for each initials field
1341
+ * to be "stamped" by the user.
1342
+ */
1343
+ const createInitials = (endpoint, name, initials) => {
1344
+ const data = new FormData();
1345
+ data.append('initial', initials, name);
1346
+ return endpoint.api //
1347
+ .post(`/initials`, data)
1348
+ .then((r) => r.data);
1453
1349
  };
1350
+
1351
+ /**
1352
+ * Update a recipient's status block
1353
+ */
1354
+ const updateRecipient = async (endpoint, envelopeId, roleName, params) => endpoint.api //
1355
+ .put(`/envelopes/${envelopeId}/recipients/${roleName}`, params)
1356
+ .then((r) => r.data);
1357
+ /**
1358
+ * Submit an envelope (signing is finished). Note that all fields must be valid/completed for this to succeed.
1359
+ */
1360
+ const envelopeRecipientSubmit = (endpoint, envelopeId, roleName) => updateRecipient(endpoint, envelopeId, roleName, { action: 'submit' });
1361
+ /**
1362
+ * Decline to complete an envelope (signing will not terminated).
1363
+ */
1364
+ const envelopeRecipientDecline = (endpoint, envelopeId, roleName) => updateRecipient(endpoint, envelopeId, roleName, { action: 'decline' });
1365
+ /**
1366
+ * Claim / change ownership of an envelope. This is a special-case operation only available in certain workflows.
1367
+ */
1368
+ const envelopeRecipientChangeOwner = (endpoint, envelopeId, roleName, email, fullName) => updateRecipient(endpoint, envelopeId, roleName, { action: 'owner_update', email, full_name: fullName });
1369
+ /**
1370
+ * Agree to electronic signing.
1371
+ */
1372
+ const envelopeRecipientAgree = (endpoint, envelopeId, roleName, agreed) => updateRecipient(endpoint, envelopeId, roleName, { action: 'update', agreed });
1454
1373
  /**
1455
- * Create a template from a Sharepoint asset.
1456
- *
1457
- * ```typescript
1458
- * import {Templates} from '@verdocs/js-sdk/Templates';
1459
- *
1460
- * const newTemplate = await Templates.createTemplateFromSharepoint((VerdocsEndpoint.getDefault(), {...});
1461
- * ```
1374
+ * Change a recipient's name.
1462
1375
  */
1463
- const createTemplateFromSharepoint = (endpoint, params) => {
1464
- const options = {
1465
- timeout: 120000,
1466
- };
1467
- return endpoint.api.post('/templates/from-sharepoint', params, options).then((r) => r.data);
1468
- };
1376
+ const envelopeRecipientUpdateName = (endpoint, envelopeId, roleName, fullName) => updateRecipient(endpoint, envelopeId, roleName, { action: 'update', new_full_name: fullName });
1469
1377
  /**
1470
- * Update a template.
1471
- *
1472
- * ```typescript
1473
- * import {Templates} from '@verdocs/js-sdk/Templates';
1474
- *
1475
- * const updatedTemplate = await Templates.updateTemplate((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9', { name: 'New Name' });
1476
- * ```
1378
+ * Change a recipient's name.
1477
1379
  */
1478
- const updateTemplate = (endpoint, templateId, params) => endpoint.api //
1479
- .put(`/templates/${templateId}`, params)
1380
+ const envelopeRecipientPrepare = (endpoint, envelopeId, roleName, recipients) => updateRecipient(endpoint, envelopeId, roleName, { action: 'prepare', recipients });
1381
+ /**
1382
+ * Get a signing token.
1383
+ */
1384
+ const getSignerToken = (endpoint, envelopeId, roleName) => endpoint.api //
1385
+ .get(`/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}/signer-token`)
1480
1386
  .then((r) => r.data);
1481
1387
  /**
1482
- * Delete a template.
1483
- *
1484
- * ```typescript
1485
- * import {Templates} from '@verdocs/js-sdk/Templates';
1486
- *
1487
- * await Templates.deleteTemplate((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9');
1488
- * ```
1388
+ * Get an in-person signing link.
1489
1389
  */
1490
- const deleteTemplate = (endpoint, templateId) => endpoint.api //
1491
- .delete(`/templates/${templateId}`)
1390
+ const getInPersonLink = (endpoint, envelopeId, roleName) => endpoint.api //
1391
+ .get(`/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}?in_person_link=true`)
1492
1392
  .then((r) => r.data);
1493
1393
  /**
1494
- * Search for templates matching various criteria.
1495
- *
1496
- * ```typescript
1497
- * import {Templates} from '@verdocs/js-sdk/Templates';
1498
- *
1499
- * const {result, page, total} = await Templates.search((VerdocsEndpoint.getDefault(), { ... });
1500
- * ```
1394
+ * Send a delegation request.
1501
1395
  */
1502
- const searchTemplates = async (endpoint, params) => endpoint.api //
1503
- .post('/templates/search', params)
1396
+ const sendDelegate = (endpoint, envelopeId, roleName) => endpoint.api //
1397
+ .post(`/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}/delegate`)
1504
1398
  .then((r) => r.data);
1505
1399
  /**
1506
- * Get a summary of template data, typically used to populate admin panel dashboard pages.
1507
- *
1508
- * ```typescript
1509
- * import {Templates} from '@verdocs/js-sdk/Templates';
1510
- *
1511
- * const summary = await Templates.getSummary((VerdocsEndpoint.getDefault(), 0);
1512
- * ```
1400
+ * Resend a recipient's invitation.
1513
1401
  */
1514
- const getTemplatesSummary = async (endpoint, params = {}) => endpoint.api //
1515
- .post('/templates/summary', params)
1402
+ const resendInvitation = (endpoint, envelopeId, roleName) => endpoint.api //
1403
+ .post(`/envelopes/${envelopeId}/recipients/${encodeURIComponent(roleName)}/resend_invitation`)
1516
1404
  .then((r) => r.data);
1517
- const cachedTemplates = {};
1405
+
1518
1406
  /**
1519
- * Wrapper for `getTemplate()` that limits queries to one every 2 seconds per template ID.
1520
- * This is intended for use in component hierarchies that all rely on the same template
1521
- * to avoid unnecessary repeat server calls.
1407
+ * Enable automatic reminders. setup_time is the number of days after the envelope is sent that the first reminder
1408
+ * should be sent. interval_time is the number of days between reminders.
1522
1409
  */
1523
- const throttledGetTemplate = (endpoint, templateId) => {
1524
- if (cachedTemplates[templateId] && cachedTemplates[templateId].loaded + 2000 < new Date().getTime()) {
1525
- return cachedTemplates[templateId].template;
1526
- }
1527
- return getTemplate(endpoint, templateId).then((template) => {
1528
- cachedTemplates[templateId] = { loaded: new Date().getTime(), template };
1529
- return template;
1530
- });
1531
- };
1410
+ const createEnvelopeReminder = (endpoint, envelopeId, params) => endpoint.api //
1411
+ .post(`/envelopes/${envelopeId}/reminder/`, params)
1412
+ .then((r) => r.data);
1532
1413
  /**
1533
- * List templates.
1534
- *
1535
- * ```typescript
1536
- * import {Templates} from '@verdocs/js-sdk/Templates';
1537
- *
1538
- * const {totals, templates} = await Templates.listTemplates((VerdocsEndpoint.getDefault(), { q: 'test', sort: 'created_at' }); * ```
1414
+ * Get the reminder configuration for an envelope.
1539
1415
  */
1540
- const listTemplates = async (endpoint, params = {}) => endpoint.api //
1541
- .post('/templates/list', params)
1416
+ const getEnvelopeReminder = (endpoint, envelopeId, reminderId) => endpoint.api //
1417
+ .get(`/envelopes/${envelopeId}/reminder/${reminderId}`)
1418
+ .then((r) => r.data);
1419
+ /**
1420
+ * Update the reminder configuration for an envelope.
1421
+ */
1422
+ const updateEnvelopeReminder = (endpoint, envelopeId, reminderId, params) => endpoint.api //
1423
+ .put(`/envelopes/${envelopeId}/reminder/${reminderId}`, params)
1424
+ .then((r) => r.data);
1425
+ /**
1426
+ * Delete the reminder configuration for an envelope.
1427
+ */
1428
+ const deleteEnvelopeReminder = (endpoint, envelopeId, reminderId) => endpoint.api //
1429
+ .delete(`/envelopes/${envelopeId}/reminder/${reminderId}`)
1542
1430
  .then((r) => r.data);
1543
1431
 
1544
1432
  /**
1545
- * A TemplateDocument represents a PDF or other attachment in a Template.
1433
+ * Various helpers to identify available operations for an envelope by a user.
1546
1434
  *
1547
1435
  * @module
1548
1436
  */
1549
1437
  /**
1550
- * Get all the Template Documents associated to a particular Template.
1551
- *
1552
- * ```typescript
1553
- * import {TemplateDocument} from '@verdocs/js-sdk/Templates';
1554
- *
1555
- * await TemplateDocument.geTemplateDocuments((VerdocsEndpoint.getDefault(), templateId);
1556
- * ```
1438
+ * Check to see if the user owns the envelope.
1557
1439
  */
1558
- const getTemplateDocuments = (endpoint, templateId) => endpoint.api //
1559
- .get(`/templates/${templateId}/documents/`)
1560
- .then((r) => r.data);
1440
+ const userIsEnvelopeOwner = (session, envelope) => envelope.profile_id === session?.profile_id;
1561
1441
  /**
1562
- * Get a specific Document.
1563
- *
1564
- * ```typescript
1565
- * import {TemplateDocument} from '@verdocs/js-sdk/Templates';
1566
- *
1567
- * await TemplateDocument.geTemplateDocument((VerdocsEndpoint.getDefault(), templateId,documentId);
1568
- * ```
1442
+ * Check to see if the user owns the envelope.
1569
1443
  */
1570
- const getTemplateDocument = (endpoint, templateId, documentId) => endpoint.api //
1571
- .get(`/templates/${templateId}/documents/${documentId}`)
1572
- .then((r) => r.data);
1444
+ const userIsEnvelopeRecipient = (session, envelope) => envelope.profile_id === session?.profile_id;
1573
1445
  /**
1574
- * Create a Document for a particular Template.
1575
- *
1576
- * ```typescript
1577
- * import {TemplateDocument} from '@verdocs/js-sdk/Templates';
1578
- *
1579
- * await TemplateDocument.createDocument((VerdocsEndpoint.getDefault(), templateID, params);
1580
- * ```
1446
+ * Check to see if the envelope has pending actions.
1581
1447
  */
1582
- const createTemplateDocument = (endpoint, templateId, file, onUploadProgress) => {
1583
- const formData = new FormData();
1584
- formData.append('document', file, file.name);
1585
- return endpoint.api //
1586
- .post(`/templates/${templateId}/documents`, formData, {
1587
- timeout: 120000,
1588
- onUploadProgress: (event) => {
1589
- const total = event.total || 1;
1590
- const loaded = event.loaded || 0;
1591
- onUploadProgress?.(Math.floor((loaded * 100) / (total || 1)), loaded, total || 1);
1592
- },
1593
- })
1594
- .then((r) => r.data);
1595
- };
1448
+ const envelopeIsActive = (envelope) => envelope.status !== 'complete' && envelope.status !== 'declined' && envelope.status !== 'canceled';
1596
1449
  /**
1597
- * Delete a specific Document.
1598
- *
1599
- * ```typescript
1600
- * import {TemplateDocument} from '@verdocs/js-sdk/Templates';
1601
- *
1602
- * await TemplateDocument.deleteDocument((VerdocsEndpoint.getDefault(), templateID, documentID);
1603
- * ```
1450
+ * Check to see if the envelope has been completed.
1604
1451
  */
1605
- const deleteTemplateDocument = (endpoint, templateId, documentId) => endpoint.api //
1606
- .delete(`/templates/${templateId}/documents/${documentId}`)
1607
- .then((r) => r.data);
1452
+ const envelopeIsComplete = (envelope) => envelope.status !== 'complete';
1608
1453
  /**
1609
- * Get (binary download) a file attached to a Template. It is important to use this method
1610
- * rather than a direct A HREF or similar link to set the authorization headers for the
1611
- * request.
1454
+ * Check to see if the user owns the envelope.
1612
1455
  */
1613
- const getTemplateDocumentFile = async (endpoint, templateId, documentId) => endpoint.api //
1614
- .get(`/templates/${templateId}/documents/${documentId}?file=true`, { responseType: 'blob' })
1615
- .then((r) => r.data);
1456
+ const userCanCancelEnvelope = (session, envelope) => userIsEnvelopeOwner(session, envelope) &&
1457
+ envelope.status !== 'complete' &&
1458
+ envelope.status !== 'declined' &&
1459
+ envelope.status !== 'canceled';
1616
1460
  /**
1617
- * Get (binary download) a file attached to a Template. It is important to use this method
1618
- * rather than a direct A HREF or similar link to set the authorization headers for the
1619
- * request.
1461
+ * Check to see if the user owns the envelope.
1620
1462
  */
1621
- const getTemplateDocumentThumbnail = async (endpoint, templateId, documentId) => endpoint.api //
1622
- .get(`/templates/${templateId}/documents/${documentId}?thumbnail=true`, { responseType: 'blob' })
1623
- .then((r) => r.data);
1463
+ const userCanFinishEnvelope = (session, envelope) => userIsEnvelopeOwner(session, envelope) &&
1464
+ envelope.status !== 'complete' &&
1465
+ envelope.status !== 'declined' &&
1466
+ envelope.status !== 'canceled';
1624
1467
  /**
1625
- * Get a display URI for a given page in a file attached to a template document. These pages are rendered server-side
1626
- * into PNG resources suitable for display in IMG tags although they may be used elsewhere. Note that these are intended
1627
- * for DISPLAY ONLY, are not legally binding documents, and do not contain any encoded metadata from participants. The
1628
- * original asset may be obtained by calling `getTemplateDocumentFile()` or similar.
1468
+ * Returns true if the recipient has a pending action. Note that this does not necessarily mean the recipient can act (yet).
1629
1469
  */
1630
- const getTemplateDocumentPageDisplayUri = async (endpoint, templateId, documentId, page) => endpoint.api.get(`/templates/${templateId}/documents/${documentId}/pages/${page}/image`).then((r) => r.data);
1470
+ const recipientHasAction = (recipient) => !['submitted', 'canceled', 'declined'].includes(recipient.status);
1471
+ /**
1472
+ * Returns the recipients who still have a pending action. Note that not all of these recipients may be able to act (yet).
1473
+ */
1474
+ const getRecipientsWithActions = (envelope) => (envelope?.recipients || []).filter(recipientHasAction);
1475
+ /**
1476
+ * Returns true if the recipient can act.
1477
+ */
1478
+ const recipientCanAct = (recipient, recipientsWithActions) => recipient.sequence === recipientsWithActions?.[0]?.sequence;
1479
+ /**
1480
+ * Returns true if the user can act.
1481
+ */
1482
+ const userCanAct = (email, recipientsWithActions) => {
1483
+ const recipient = recipientsWithActions.find((r) => r.email === email);
1484
+ return recipient && recipient.sequence === recipientsWithActions?.[0]?.sequence;
1485
+ };
1486
+ /**
1487
+ * Returns true if the user can act.
1488
+ */
1489
+ const userCanSignNow = (session, envelope) => {
1490
+ if (!session) {
1491
+ return false;
1492
+ }
1493
+ const recipientsWithActions = getRecipientsWithActions(envelope);
1494
+ const myRecipient = recipientsWithActions.find((r) => r.profile_id === session?.profile_id || r.email === session?.email);
1495
+ return (myRecipient &&
1496
+ envelopeIsActive(envelope) &&
1497
+ userIsEnvelopeRecipient(session, envelope) &&
1498
+ recipientCanAct(myRecipient, recipientsWithActions));
1499
+ };
1500
+ const getNextRecipient = (envelope) => {
1501
+ const recipientsWithActions = getRecipientsWithActions(envelope);
1502
+ return recipientsWithActions?.[0];
1503
+ };
1631
1504
 
1632
1505
  /**
1633
- * Get all defined validators
1634
- *
1635
- * ```typescript
1636
- * import {Documents} from '@verdocs/js-sdk/Templates';
1637
- *
1638
- * await Documents.getDocuments(templateID);
1639
- * ```
1506
+ * Create a signature block. In a typical signing workflow, the user is asked at the beginning of the process to "adopt"
1507
+ * a signature block to be used for all signature fields in the document. Thus, this is typically called one time to
1508
+ * create and store a signature block. Thereafter, the ID of the signature block may be re-used for each signature field
1509
+ * to be "stamped" by the user.
1640
1510
  */
1641
- const getValidators = (endpoint) => endpoint.api //
1642
- .get('/validators')
1643
- .then((r) => r.data);
1644
- const getValidator = (endpoint, validatorName) => endpoint.api //
1645
- .get(`/validators/${validatorName}`)
1511
+ const createSignature = (endpoint, name, signature) => {
1512
+ const data = new FormData();
1513
+ data.append('signature', signature, name);
1514
+ return endpoint.api //
1515
+ .post(`/signatures`, data)
1516
+ .then((r) => r.data);
1517
+ };
1518
+ /**
1519
+ * Get the availbable signatures for a user.
1520
+ */
1521
+ const getSignatures = (endpoint) => endpoint.api //
1522
+ .get('/signatures')
1646
1523
  .then((r) => r.data);
1647
- const EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
1648
- const isValidEmail = (email) => !!email && EMAIL_REGEX.test(email);
1649
- // @see https://www.regextester.com/1978
1650
- const PHONE_REGEX = /((?:\+|00)[17](?: |\-)?|(?:\+|00)[1-9]\d{0,2}(?: |\-)?|(?:\+|00)1\-\d{3}(?: |\-)?)?(0\d|\([0-9]{3}\)|[1-9]{0,3})(?:((?: |\-)[0-9]{2}){4}|((?:[0-9]{2}){4})|((?: |\-)[0-9]{3}(?: |\-)[0-9]{4})|([0-9]{7}))/;
1651
- const isValidPhone = (phone) => !!phone && PHONE_REGEX.test(phone);
1652
- const isValidRoleName = (value, roles) => roles.findIndex((role) => role.name === value) !== -1;
1653
- const TagRegEx = /^[a-zA-Z0-9-]{0,32}$/;
1654
- const isValidTag = (value, tags) => TagRegEx.test(value) || tags.findIndex((tag) => tag === value) !== -1;
1655
-
1656
1524
  /**
1657
- * Authenticate to Verdocs via user/password authentication
1658
- *
1659
- * ```typescript
1660
- * import {Auth} from '@verdocs/js-sdk/Auth';
1661
- * import {Transport} from '@verdocs/js-sdk/HTTP';
1662
- *
1663
- * const {accessToken} = await Auth.authenticateUser({ username: 'test@test.com', password: 'PASSWORD' });
1664
- * Transport.setAuthToken(accessToken);
1665
- * ```
1525
+ * Get a user's signature by ID.
1666
1526
  */
1667
- const authenticateUser = (endpoint, params) => endpoint.api //
1668
- .post('/authentication/login', params)
1527
+ const getSignature = (endpoint, signatureId) => endpoint.api //
1528
+ .get(`/signatures/${signatureId}`)
1669
1529
  .then((r) => r.data);
1670
1530
  /**
1671
- * Authenticate to Verdocs via client ID / Secret authentication. **NOTE: This is only suitable for
1672
- * NodeJS server-side applications. Never expose your Client Secret in a Web or Mobile app!** Also note
1673
- * that access tokens may be cached by server-side apps (and this is recommended) but do expire after 2
1674
- * hours. This expiration may change based on future security needs. Application developers are encouraged
1675
- * to check the `exp` expiration field in the response accessToken and renew tokens after they expire.
1676
- *
1677
- * ```typescript
1678
- * import {Auth} from '@verdocs/js-sdk/Auth';
1679
- * import {Transport} from '@verdocs/js-sdk/HTTP';
1680
- *
1681
- * const {accessToken} = await Auth.authenticateApp({ client_id: 'CLIENTID', client_secret: 'SECRET' });
1682
- * Transport.setAuthToken(accessToken);
1683
- * ```
1531
+ * Delete a user's signature.
1684
1532
  */
1685
- const authenticateApp = (endpoint, params) => endpoint.api //
1686
- .post('/authentication/login_client', {}, { headers: params })
1533
+ const deleteSignature = (endpoint, signatureId) => endpoint.api //
1534
+ .delete(`/signatures/${signatureId}`)
1687
1535
  .then((r) => r.data);
1536
+
1688
1537
  /**
1689
- * Validate a token. Only Verdocs tokens will be accepted. Most applications can decode tokens locally,
1690
- * because tokens will be validated when API calls are made anyway. However, high-security applications
1691
- * may use this endpoint to check if a token has been revoked.
1538
+ * API keys are used to authenticate server-to-server calls. (API keys should **never** be used for client-to-server operations!)
1539
+ * To generate a key, either use the Verdocs admin interface and make note of the client_id and client_secret generated, or call
1540
+ * createKey as shown below. Then call {@link Users.Auth.authenticateApp} to obtain an access token using the provided ID and
1541
+ * secret. Note that server-to-server authentication requests return shorter-lived tokens, so it is important to check the `exp`
1542
+ * field and re-authenticate as needed for subsequent calls.
1692
1543
  *
1693
- * ```typescript
1694
- * import {Auth} from '@verdocs/js-sdk/Auth';
1544
+ * API keys may be updated or rotated at any time. Regular rotation is recommended. Rotation will not expire or invalidate
1545
+ * existing server-to-server sessions, so it may be done at any time without disrupting your application.
1695
1546
  *
1696
- * const {valid} = await Auth.validateToken({ token });
1697
- * if (!valid) {
1698
- * window.alert('Session invalid or expired. Please re-authenticate.');
1699
- * }
1700
- * ```
1547
+ * @module
1701
1548
  */
1702
- const validateToken = (endpoint, params) => endpoint.api //
1703
- .post('/token/isValid', params)
1704
- .then((r) => r.data);
1705
1549
  /**
1706
- * If called before the session expires, this will refresh the caller's session and tokens.
1550
+ * Get a list of keys for a given organization. The caller must have admin access to the organization.
1707
1551
  *
1708
1552
  * ```typescript
1709
- * import {Auth} from '@verdocs/js-sdk/Auth';
1710
- * import {Transport} from '@verdocs/js-sdk/HTTP';
1553
+ * import {ApiKeys} from '@verdocs/js-sdk/Organizations';
1711
1554
  *
1712
- * const {accessToken} = await Auth.refreshTokens();
1713
- * Transport.setAuthToken(accessToken);
1555
+ * const keys = await ApiKeys.getKeys(ORGID);
1714
1556
  * ```
1715
1557
  */
1716
- const refreshTokens = (endpoint) => endpoint.api //
1717
- .get('/token')
1558
+ const getApiKeys = (endpoint, organizationId) => endpoint.api //
1559
+ .get(`/organizations/${organizationId}/api_key`)
1718
1560
  .then((r) => r.data);
1719
1561
  /**
1720
- * Update the caller's password. To help prevent CSRF attack vectors, the user's old password and email address are required.
1562
+ * Create an API key.
1721
1563
  *
1722
1564
  * ```typescript
1723
- * import {Auth} from '@verdocs/js-sdk/Auth';
1565
+ * import {ApiKeys} from '@verdocs/js-sdk/Organizations';
1724
1566
  *
1725
- * const {status, message} = await Auth.updatePassword({ email, oldPassword, newPassword });
1726
- * if (status !== 'OK') {
1727
- * window.alert(`Password reset error: ${message}`);
1728
- * }
1567
+ * await ApiKeys.createKey(ORGID, {name: NEWNAME});
1729
1568
  * ```
1730
1569
  */
1731
- const updatePassword = (endpoint, params) => endpoint.api //
1732
- .put('/user/update_password', params)
1570
+ const createApiKey = (endpoint, organizationId, params) => endpoint.api //
1571
+ .post(`/organizations/${organizationId}/api_key`, params)
1733
1572
  .then((r) => r.data);
1734
1573
  /**
1735
- * Reset the caller's password.
1574
+ * Rotate the secret for an API key. The caller must have admin access to the organization.
1736
1575
  *
1737
1576
  * ```typescript
1738
- * import {Auth} from '@verdocs/js-sdk/Auth';
1577
+ * import {ApiKeys} from '@verdocs/js-sdk/Organizations';
1739
1578
  *
1740
- * const {success} = await Auth.resetPassword({ email });
1741
- * if (status !== 'OK') {
1742
- * window.alert(`Please check your email for instructions on how to reset your password.`);
1743
- * }
1579
+ * const {client_secret: newSecret} = await ApiKeys.rotateKey(ORGID, CLIENTID);
1744
1580
  * ```
1745
1581
  */
1746
- const resetPassword = (endpoint, params) => endpoint.api //
1747
- .post('/user/reset_password', params)
1582
+ const rotateApiKey = (endpoint, organizationId, clientId) => endpoint.api //
1583
+ .put(`/organizations/${organizationId}/api_key/${clientId}/rotate`)
1748
1584
  .then((r) => r.data);
1749
1585
  /**
1750
- * Update the caller's email address.
1586
+ * Update an API key to change its assigned Profile ID or Name.
1751
1587
  *
1752
1588
  * ```typescript
1753
- * import {Auth} from '@verdocs/js-sdk/Auth';
1589
+ * import {ApiKeys} from '@verdocs/js-sdk/Organizations';
1754
1590
  *
1755
- * const {profiles} = await Auth.updateEmail({ email: newEmail });
1591
+ * await ApiKeys.updateKey(ORGID, CLIENTID, {name: NEWNAME});
1756
1592
  * ```
1757
1593
  */
1758
- const updateEmail = (endpoint, params) => endpoint.api //
1759
- .put('/user/update_email', params)
1594
+ const updateApiKey = (endpoint, organizationId, clientId, params) => endpoint.api //
1595
+ .patch(`/organizations/${organizationId}/api_key/${clientId}`, params)
1760
1596
  .then((r) => r.data);
1761
1597
  /**
1762
- * Resend the email verification request. Note that to prevent certain forms of abuse, the email address is not
1763
- * a parameter here. Instead, the caller must be authenticated as the (unverified) user. To simplify this process,
1764
- * the access token to be used may be passed directly as a parameter here. This avoids the need to set it as the
1765
- * active token on an endpoint, which may be inconvenient in workflows where it is preferable to keep the user in
1766
- * "anonymous" mode while verification is being performed.
1598
+ * Delete an API key.
1767
1599
  *
1768
1600
  * ```typescript
1769
- * import {Auth} from '@verdocs/js-sdk/Auth';
1601
+ * import {ApiKeys} from '@verdocs/js-sdk/Organizations';
1770
1602
  *
1771
- * const result = await Auth.resendVerification();
1603
+ * await ApiKeys.deleteKey(ORGID, CLIENTID);
1772
1604
  * ```
1773
1605
  */
1774
- const resendVerification = (endpoint, accessToken) => endpoint.api //
1775
- .post('/user/email_verification', {}, accessToken ? { headers: { Authorization: `Bearer ${accessToken}` } } : {})
1776
- .then((r) => r.data);
1777
- const createUser = (endpoint, params) => endpoint.api //
1778
- .post('/user', params)
1779
- .then((r) => r.data);
1780
-
1781
- // TODO
1782
- const billingPlaceholder = {};
1783
-
1784
- const getNotifications = async (endpoint) => endpoint.api //
1785
- .get('/notifications')
1606
+ const deleteApiKey = (endpoint, organizationId, clientId) => endpoint.api //
1607
+ .delete(`/organizations/${organizationId}/api_key/${clientId}`)
1786
1608
  .then((r) => r.data);
1787
1609
 
1788
1610
  /**
1789
- * Get the user's available profiles. The current profile will be marked with `current: true`.
1790
- *
1791
- * ```typescript
1792
- * import {Profiles} from '@verdocs/js-sdk/Users';
1611
+ * Organizations may contain "Groups" of user profiles, called Members. Groups may have permissions assigned that
1612
+ * apply to all Members, making it easy to configure role-based access control (RBAC) within an Organization. Note
1613
+ * that permissions are **additive**. A user may be a member of more than one group, and may also have permissions
1614
+ * assigned directly. In that case, the user will have the combined set of all permissions inherited from all
1615
+ * sources.
1793
1616
  *
1794
- * const profiles = await Profiles.getProfiles()
1795
- * ```
1617
+ * @module
1796
1618
  */
1797
- const getProfiles = (endpoint) => endpoint.api //
1798
- .get('/profiles')
1799
- .then((r) => r.data);
1800
1619
  /**
1801
- * Get the user's available profiles. The current profile will be marked with `current: true`.
1620
+ * Get a list of groups for a given organization. The caller must have admin access to the organization.
1802
1621
  *
1803
1622
  * ```typescript
1804
- * import {Profiles} from '@verdocs/js-sdk/Users';
1623
+ * import {Groups} from '@verdocs/js-sdk/Organizations';
1805
1624
  *
1806
- * const profiles = await Profiles.getCurrentProfile()
1625
+ * const groups = await Groups.getGroups(ORGID);
1807
1626
  * ```
1808
1627
  */
1809
- const getCurrentProfile = (endpoint) => endpoint.api //
1810
- .get('/profiles')
1811
- .then((r) => (r.data || []).find((profile) => profile.current));
1628
+ const getGroups = (endpoint, organizationId) => endpoint.api //
1629
+ .get(`/organizations/${organizationId}/groups`)
1630
+ .then((r) => r.data);
1812
1631
  /**
1813
- * Get a list of system roles.
1632
+ * Get a single group by name. Returns a detail record.
1814
1633
  *
1815
1634
  * ```typescript
1816
- * import {Profiles} from '@verdocs/js-sdk/Users';
1635
+ * import {Groups} from '@verdocs/js-sdk/Organizations';
1817
1636
  *
1818
- * const roles = await Profiles.getRoles();
1637
+ * const groups = await Groups.getGroups(ORGID);
1819
1638
  * ```
1820
1639
  */
1821
- const getRoles = (endpoint) => endpoint.api //
1822
- .get('/roles')
1640
+ const getGroupByName = (endpoint, organizationId, name) => endpoint.api //
1641
+ .get(`/organizations/${organizationId}/groups`, { params: { name } })
1823
1642
  .then((r) => r.data);
1824
1643
  /**
1825
- * Get a list of system roles.
1644
+ * Get the details for a group.
1826
1645
  *
1827
1646
  * ```typescript
1828
- * import {Profiles} from '@verdocs/js-sdk/Users';
1647
+ * import {Groups} from '@verdocs/js-sdk/Organizations';
1829
1648
  *
1830
- * const permissions = await Profiles.getPermissions();
1649
+ * const groups = await Groups.getGroups(ORGID);
1831
1650
  * ```
1832
1651
  */
1833
- const getPermissions = (endpoint) => endpoint.api //
1834
- .get('/permissions')
1652
+ const getGroup = (endpoint, organizationId, groupId) => endpoint.api //
1653
+ .get(`/organizations/${organizationId}/groups/${groupId}`)
1654
+ .then((r) => r.data);
1655
+ const getGroupMembers = (endpoint, organizationId, groupId) => endpoint.api //
1656
+ .get(`/organizations/${organizationId}/groups/${groupId}/members`)
1657
+ .then((r) => r.data);
1658
+ const addGroupMembers = (endpoint, organizationId, groupId, params) => endpoint.api //
1659
+ .post(`/organizations/${organizationId}/groups/${groupId}/members`, params)
1660
+ .then((r) => r.data);
1661
+ const deleteGroupMembers = (endpoint, organizationId, groupId, params) => endpoint.api //
1662
+ .put(`/organizations/${organizationId}/groups/${groupId}/delete_members`, params)
1663
+ .then((r) => r.data);
1664
+ const addGroupPermission = (endpoint, organizationId, groupId, permission) => endpoint.api //
1665
+ .post(`/organizations/${organizationId}/groups/${groupId}/permissions/${permission}`, {})
1835
1666
  .then((r) => r.data);
1667
+ const deleteGroupPermission = (endpoint, organizationId, groupId, permission) => endpoint.api //
1668
+ .delete(`/organizations/${organizationId}/groups/${groupId}/permissions/${permission}`)
1669
+ .then((r) => r.data);
1670
+
1836
1671
  /**
1837
- * Create a profile. If the caller does not have a "current" profile set, the new profile will be made current.
1672
+ * An invitation represents an opportunity for a Member to join an Organization.
1838
1673
  *
1839
- * ```typescript
1840
- * import {Profiles} from '@verdocs/js-sdk/Users';
1674
+ * @module
1675
+ */
1676
+ const getOrganizationInvitations = (endpoint, organizationId) => endpoint.api //
1677
+ .get(`/organizations/${organizationId}/invitation`)
1678
+ .then((r) => r.data);
1679
+ const createOrganizationInvitation = (endpoint, organizationId, params) => endpoint.api //
1680
+ .post(`/organizations/${organizationId}/invitation`, params)
1681
+ .then((r) => r.data);
1682
+ const deleteOrganizationInvitation = (endpoint, organizationId, email) => endpoint.api //
1683
+ .delete(`/organizations/${organizationId}/invitation/${email}`)
1684
+ .then((r) => r.data);
1685
+ const updateOrganizationInvitation = (endpoint, organizationId, email, params) => endpoint.api //
1686
+ .patch(`/organizations/${organizationId}/invitation/${email}`, params)
1687
+ .then((r) => r.data);
1688
+ const resendOrganizationInvitation = (endpoint, organizationId, email) => endpoint.api //
1689
+ .post(`/organizations/${organizationId}/invitation/${email}/resend`)
1690
+ .then((r) => r.data);
1691
+ const getOrganizationInvitation = (endpoint, organizationId, email, token) => endpoint.api //
1692
+ .get(`/organizations/${organizationId}/invitation/${email}/accept/${token}`)
1693
+ .then((r) => r.data);
1694
+ const acceptOrganizationInvitation = (endpoint, organizationId, email, token) => endpoint.api //
1695
+ .post(`/organizations/${organizationId}/invitation/${email}/accept/${token}`)
1696
+ .then((r) => r.data);
1697
+ const declineOrganizationInvitation = (endpoint, organizationId, email, token) => endpoint.api //
1698
+ .post(`/organizations/${organizationId}/invitation/${email}/decline/${token}`)
1699
+ .then((r) => r.data);
1700
+ const claimNewUser = (endpoint, organizationId, email, token) => endpoint.api //
1701
+ .put(`/organizations/${organizationId}/invitation/${email}/token/${token}/new_user`)
1702
+ .then((r) => r.data);
1703
+
1704
+ /**
1705
+ * An Organization Member (aka Profile) is an individual user with access to an organization.
1841
1706
  *
1842
- * const newProfile = await Profiles.createProfile({ first_name: 'FIRST', last_name: 'LAST', email: 'EMAIL' });
1843
- * ```
1707
+ * @module
1844
1708
  */
1845
- const createProfile = (endpoint, params) => endpoint.api //
1846
- .post('/profiles', params)
1709
+ const getOrganizationMembers = (endpoint, organizationId) => endpoint.api //
1710
+ .get(`/organizations/${organizationId}/profiles`)
1711
+ .then((r) => r.data);
1712
+ const deleteOrganizationMember = (endpoint, organizationId, profileId) => endpoint.api //
1713
+ .delete(`/organizations/${organizationId}/profiles/${profileId}`)
1714
+ .then((r) => r.data);
1715
+ const addOrganizationMemberRole = (endpoint, organizationId, profileId, roleId) => endpoint.api //
1716
+ .post(`/organizations/${organizationId}/profiles/${profileId}/role/${roleId}`)
1717
+ .then((r) => r.data);
1718
+ const deleteOrganizationMemberRole = (endpoint, organizationId, profileId, roleId) => endpoint.api //
1719
+ .delete(`/organizations/${organizationId}/profiles/${profileId}/role/${roleId}`)
1847
1720
  .then((r) => r.data);
1848
- /**
1849
- * Get a profile. The caller must have admin access to the given profile.
1850
- * TODO: Add a "public" profile endpoint for public pages
1851
- *
1852
- * ```typescript
1853
- * import {Profiles} from '@verdocs/js-sdk/Users';
1854
- *
1855
- * const profile = await Profiles.getProfile('PROFILEID');
1856
- * ```
1857
- */
1858
- const getProfile = (endpoint, profileId) => endpoint.api //
1859
- .get(`/profiles/${profileId}`)
1721
+ const getOrganizationMemberPlans = (endpoint, organizationId, profileId) => endpoint.api //
1722
+ .get(`/organizations/${organizationId}/profiles/${profileId}/plans`)
1860
1723
  .then((r) => r.data);
1724
+
1861
1725
  /**
1862
- * Get a profile's permissions. The caller must have admin access to the given profile.
1863
- *
1864
- * ```typescript
1865
- * import {Profiles} from '@verdocs/js-sdk/Users';
1726
+ * An Organization is the top level object for ownership for Members, Documents, and Templates.
1866
1727
  *
1867
- * const permissions = await Profiles.getProfilePermissions('PROFILEID');
1868
- * ```
1728
+ * @module
1869
1729
  */
1870
- const getProfilePermissions = (endpoint, profileId) => endpoint.api //
1871
- .get(`/profiles/${profileId}/permissions`)
1872
- .then((r) => r.data);
1873
1730
  /**
1874
- * Get a profile's groups.
1875
- *
1876
- * ```typescript
1877
- * import {Profiles} from '@verdocs/js-sdk/Users';
1878
- *
1879
- * const groups = await Profiles.getProfileGroups('PROFILEID');
1880
- * ```
1731
+ * Get a list of organizations the user has access to.
1881
1732
  */
1882
- const getProfileGroups = (endpoint, profileId) => endpoint.api //
1883
- .get(`/profiles/${profileId}/groups`)
1733
+ const getOrganizations = (endpoint) => endpoint.api //
1734
+ .get('/organizations')
1884
1735
  .then((r) => r.data);
1885
1736
  /**
1886
- * Switch the caller's "current" profile. The current profile is used for permissions checking and profile_id field settings
1887
- * for most operations in Verdocs. It is important to select the appropropriate profile before calling other API functions.
1888
- *
1889
- * ```typescript
1890
- * import {Profiles} from '@verdocs/js-sdk/Users';
1891
- *
1892
- * const newProfile = await Profiles.switchProfile('PROFILEID');
1893
- * ```
1737
+ * Create an organization.
1894
1738
  */
1895
- const switchProfile = (endpoint, profileId) => endpoint.api //
1896
- .post(`/profiles/${profileId}/switch`)
1739
+ const createOrganization = (endpoint) => endpoint.api //
1740
+ .post('/organizations')
1897
1741
  .then((r) => r.data);
1898
1742
  /**
1899
- * Update a profile. For future expansion, the profile ID to update is required, but currently this must also be the
1900
- * "current" profile for the caller.
1901
- *
1902
- * ```typescript
1903
- * import {Profiles} from '@verdocs/js-sdk/Users';
1904
- *
1905
- * const newProfile = await Profiles.updateProfile('PROFILEID');
1906
- * ```
1743
+ * Delete an organization.
1907
1744
  */
1908
- const updateProfile = (endpoint, profileId, params) => endpoint.api //
1909
- .put(`/profiles/${profileId}`, params)
1745
+ const deleteOrganization = (endpoint, organizationId) => endpoint.api //
1746
+ .delete(`/organizations/${organizationId}`)
1910
1747
  .then((r) => r.data);
1911
1748
  /**
1912
- * Delete a profile. If the requested profile is the caller's curent profile, the next available profile will be selected.
1913
- *
1914
- * ```typescript
1915
- * import {Profiles} from '@verdocs/js-sdk/Users';
1916
- *
1917
- * await Profiles.deleteProfile('PROFILEID');
1918
- * ```
1749
+ * Get an organization by ID.
1919
1750
  */
1920
- const deleteProfile = (endpoint, profileId) => endpoint.api //
1921
- .delete(`/profiles/${profileId}`)
1751
+ const getOrganization = (endpoint, organizationId) => endpoint.api //
1752
+ .get(`/organizations/${organizationId}`)
1922
1753
  .then((r) => r.data);
1923
1754
  /**
1924
- * Create a user account and parent organization. This endpoint is for creating a new organization. Users joining an
1925
- * existing organization should be invited, and follow their invitation links/instructions to create their accounts.
1926
- *
1927
- * ```typescript
1928
- * import {Profiles} from '@verdocs/js-sdk/Users';
1929
- *
1930
- * const newAccount = await Profiles.createBusinessAccount({
1931
- * orgName: 'ORG', email: 'a@b.com', password: '12345678', firstName: 'FIRST', lastName: 'LAST'
1932
- * });
1933
- * ```
1755
+ * Update an organization.
1934
1756
  */
1935
- const createBusinessAccount = (endpoint, params) => endpoint.api //
1936
- .post('/user/business', params)
1757
+ const updateOrganization = (endpoint, organizationId, params) => endpoint.api //
1758
+ .patch(`/organizations/${organizationId}`, params)
1937
1759
  .then((r) => r.data);
1938
- const recordSignupSurvey = (endpoint, params) => endpoint.api //
1939
- .post('/user/signup', params)
1760
+
1761
+ const getWebhooks = (endpoint) => endpoint.api //
1762
+ .get(`/v2/webhooks/organization`)
1763
+ .then((r) => r.data);
1764
+ const setWebhooks = (endpoint, params) => endpoint.api //
1765
+ .post(`/v2/webhooks/organization`, params)
1940
1766
  .then((r) => r.data);
1941
1767
 
1942
1768
  /**
1943
- * Given a `rgba(r,g,b,a)` string value, returns the hex equivalent, dropping the alpha channel.
1944
- */
1945
- function getRGB(rgba) {
1946
- const rgbNumbers = rgba.replace('rgba(', '').replace(')', '').split(',');
1947
- const rgbObject = {
1948
- red: +rgbNumbers[0],
1949
- green: +rgbNumbers[1],
1950
- blue: +rgbNumbers[2],
1951
- alpha: +rgbNumbers[3],
1952
- };
1953
- const alpha = 1 - rgbObject.alpha;
1954
- const red = Math.round((rgbObject.alpha * (rgbObject.red / 255) + alpha) * 255);
1955
- const green = Math.round((rgbObject.alpha * (rgbObject.green / 255) + alpha) * 255);
1956
- const blue = Math.round((rgbObject.alpha * (rgbObject.blue / 255) + alpha) * 255);
1957
- return '#' + rgbToHex(red) + rgbToHex(green) + rgbToHex(blue);
1958
- }
1959
- /**
1960
- * Given an RGB string value, returns the hex equivalent.
1961
- */
1962
- function rgbToHex(rgb) {
1963
- const hex = rgb.toString(16);
1964
- if (hex.length < 2) {
1965
- return '0' + hex;
1966
- }
1967
- return hex;
1968
- }
1969
- /**
1970
- * Given a signer role index, return the color code for that signer.
1971
- */
1972
- function getRGBA(roleIndex) {
1973
- switch (roleIndex % 10) {
1974
- case 0:
1975
- return roleIndex === 0 ? 'rgba(255, 193, 7, 0.4)' : 'rgba(134, 134, 134, 0.3)'; // #FFE69C
1976
- case 1:
1977
- return 'rgba(156, 39, 176, .4)'; // '#E3C3E9'
1978
- case 2:
1979
- return 'rgba(33, 150, 243, .4)'; // '#C1E1FB'
1980
- case 3:
1981
- return 'rgba(220, 231, 117, 0.3)';
1982
- case 4:
1983
- return 'rgba(121, 134, 203, 0.3)';
1984
- case 5:
1985
- return 'rgba(77, 182, 172, 0.3)';
1986
- case 6:
1987
- return 'rgba(255, 202, 165, 0.3)';
1988
- case 7:
1989
- return 'rgba(2, 247, 190, 0.3)';
1990
- case 8:
1991
- return 'rgba(255, 138, 101, 0.3)';
1992
- case 9:
1993
- return 'rgba(82, 255, 79, 0.3)';
1994
- default:
1995
- return 'rgba(229, 115, 155, 0.3)';
1996
- }
1997
- }
1998
- /**
1999
- * Given a role name, return a color code for it. This works by computing a hash code so the specific color returned
2000
- * is not specified explicitly, but will be the same for every call with the same input value.
2001
- */
2002
- function nameToRGBA(str) {
2003
- if (!!str) {
2004
- const validNum = parseInt(str.slice(-1), 10);
2005
- if (!isNaN(validNum)) {
2006
- str += (validNum * 99).toString();
2007
- }
2008
- let hash = 0;
2009
- for (let i = 0; i < str.length; i++) {
2010
- // tslint:disable-next-line:no-bitwise
2011
- hash = str.charCodeAt(i) + ((hash << 5) - hash);
2012
- }
2013
- hash = Math.round(hash / 1.3);
2014
- // tslint:disable-next-line:no-bitwise
2015
- const c = (hash & 0x00ffff08).toString(16).toUpperCase();
2016
- const hex = '#' + '00000'.substring(0, 6 - c.length) + c;
2017
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
2018
- const color = {
2019
- r: parseInt(result[1], 16),
2020
- g: parseInt(result[2], 16),
2021
- b: parseInt(result[3], 16),
2022
- };
2023
- return `rgba(${color.r}, ${color.g}, ${color.b}, 0.2)`;
2024
- }
2025
- }
2026
- /**
2027
- * Helper function to obtain a color code given a role name given various possible inputs.
1769
+ * Confirm whether the user has all of the specified permissions.
2028
1770
  */
2029
- function getRoleColor(name, roles, index) {
2030
- if (index) {
2031
- return getRGBA(index);
2032
- }
2033
- else if (roles && roles.length > 0) {
2034
- const roleIndex = roles.findIndex((role) => role === name);
2035
- if (roleIndex > -1) {
2036
- return getRGBA(roleIndex);
2037
- }
2038
- else {
2039
- return nameToRGBA(name);
2040
- }
2041
- }
2042
- else {
2043
- return nameToRGBA(name);
2044
- }
2045
- }
1771
+ const userHasPermissions = (session, permissions) => permissions.every((perm) => (session?.permissions || []).includes(perm));
2046
1772
 
2047
- const YEAR = 365 * 24 * 60 * 60;
2048
- // const MONTH = 30 * 24 * 60 * 60;
2049
- const WEEK = 7 * 24 * 60 * 60;
2050
- const DAY = 24 * 60 * 60;
2051
- const HOUR = 60 * 60;
2052
- const MINUTE = 60;
2053
- const formatShortTimeAgo = (val) => {
2054
- if (val === undefined || val === null) {
2055
- return '';
2056
- }
2057
- let dateInput;
2058
- if (typeof val === 'string' || typeof val === 'number') {
2059
- dateInput = new Date(val);
2060
- }
2061
- else if (typeof val === 'object') {
2062
- dateInput = val;
2063
- }
2064
- else {
2065
- return '';
2066
- }
2067
- const timeDiff = Math.floor((new Date().getTime() - dateInput.getTime()) / 1000);
2068
- if (timeDiff >= YEAR) {
2069
- return Math.floor(timeDiff / YEAR) + 'Y';
2070
- }
2071
- // if (timeDiff >= MONTH) {
2072
- // return Math.floor(timeDiff / MONTH) + 'M';
2073
- // }
2074
- if (timeDiff >= WEEK) {
2075
- return Math.floor(timeDiff / WEEK) + 'W';
2076
- }
2077
- if (timeDiff >= DAY) {
2078
- return Math.floor(timeDiff / DAY) + 'D';
2079
- }
2080
- if (timeDiff >= HOUR) {
2081
- return Math.floor(timeDiff / HOUR) + 'H';
2082
- }
2083
- if (timeDiff >= MINUTE) {
2084
- return Math.floor(timeDiff / MINUTE) + 'M';
2085
- }
2086
- return `${timeDiff}S`;
2087
- };
2088
- function timePeriod(type) {
2089
- let endDate = new Date().getTime();
2090
- const today = new Date();
2091
- const month = today.getMonth();
2092
- const year = today.getFullYear();
2093
- let startDate = null;
2094
- switch (type) {
2095
- case '30d':
2096
- startDate = endDate - 60 * 60 * 24 * 30 * 1000;
1773
+ const canPerformTemplateAction = (session, action, template) => {
1774
+ if (!template && !action.includes('create')) {
1775
+ return { canPerform: false, message: 'Missing required template object' };
1776
+ }
1777
+ // We use BOGUS here to force the option-chain in things like template?.profile_id to NOT match profile?.profile_id because if both
1778
+ // were undefined, they would actually match.
1779
+ const profile_id = session?.profile_id || 'BOGUS';
1780
+ const organization_id = session?.organization_id || 'BOGUS';
1781
+ if (!profile_id) {
1782
+ return { canPerform: false, message: 'Active session required' };
1783
+ }
1784
+ const isCreator = template?.profile_id === profile_id;
1785
+ const isSameOrg = template?.organization_id === organization_id;
1786
+ const isPersonal = template?.is_personal ?? false;
1787
+ const isPublic = template?.is_public ?? false;
1788
+ const permissionsRequired = [];
1789
+ switch (action) {
1790
+ case 'create_personal':
1791
+ permissionsRequired.push('template:creator:create:personal');
2097
1792
  break;
2098
- case '60d':
2099
- startDate = endDate - 60 * 60 * 24 * 60 * 1000;
1793
+ case 'create_org':
1794
+ permissionsRequired.push('template:creator:create:org');
2100
1795
  break;
2101
- case '6m':
2102
- startDate = endDate - 60 * 60 * 24 * 30 * 6 * 1000;
1796
+ case 'create_public':
1797
+ permissionsRequired.push('template:creator:create:public');
2103
1798
  break;
2104
- case 'this_month':
2105
- startDate = new Date(year, month, 1).getTime();
1799
+ case 'read':
1800
+ if (!isCreator) {
1801
+ if ((!isPersonal && isSameOrg) || !isPublic) {
1802
+ permissionsRequired.push('template:member:read');
1803
+ }
1804
+ }
2106
1805
  break;
2107
- case 'last_month':
2108
- startDate = new Date(year, month - 1, 1).getTime();
2109
- endDate = new Date(year, month, 0).getTime();
1806
+ case 'write':
1807
+ if (!isCreator) {
1808
+ permissionsRequired.push('template:member:read');
1809
+ permissionsRequired.push('template:member:write');
1810
+ }
2110
1811
  break;
2111
- case 'this_year':
2112
- startDate = new Date(year, 0, 1);
1812
+ case 'change_visibility_personal':
1813
+ if (isCreator) {
1814
+ permissionsRequired.push('template:creator:create:personal');
1815
+ }
1816
+ else {
1817
+ permissionsRequired.push('template:member:visibility');
1818
+ }
1819
+ break;
1820
+ case 'change_visibility_org':
1821
+ if (isCreator) {
1822
+ permissionsRequired.push('template:creator:create:org');
1823
+ }
1824
+ else {
1825
+ permissionsRequired.push('template:member:visibility');
1826
+ }
1827
+ break;
1828
+ case 'change_visibility_public':
1829
+ if (isCreator) {
1830
+ permissionsRequired.push('template:creator:create:public');
1831
+ permissionsRequired.push('template:creator:visibility');
1832
+ }
1833
+ else {
1834
+ permissionsRequired.push('template:member:visibility');
1835
+ }
1836
+ break;
1837
+ case 'delete':
1838
+ if (isCreator) {
1839
+ permissionsRequired.push('template:creator:delete');
1840
+ }
1841
+ else {
1842
+ permissionsRequired.push('template:member:delete');
1843
+ }
2113
1844
  break;
2114
- case 'all_time':
2115
1845
  default:
2116
- return null;
1846
+ return { canPerform: false, message: 'Action is not defined' };
2117
1847
  }
2118
- if (startDate === null && endDate === null) {
2119
- return null;
1848
+ if (hasRequiredPermissions(session, permissionsRequired)) {
1849
+ return { canPerform: true, message: '' };
2120
1850
  }
2121
- return {
2122
- start_time: new Date(startDate).toISOString(),
2123
- end_time: new Date(endDate).toISOString(),
2124
- };
2125
- }
1851
+ return { canPerform: false, message: `Insufficient access to perform '${action}'. Needed permissions: ${permissionsRequired.toString()}` };
1852
+ };
1853
+ const hasRequiredPermissions = (session, permissions) => permissions.every((perm) => (session?.permissions || []).includes(perm));
2126
1854
 
2127
- function getRTop(y, fieldHeight, iTextHeight, yRatio) {
2128
- return iTextHeight - (y + fieldHeight) * yRatio;
2129
- }
2130
- function getRLeft(x, ratio) {
2131
- return x * ratio;
2132
- }
2133
- function getRValue(y, ratio) {
2134
- return y * ratio;
2135
- }
2136
- function blobToBase64(image) {
2137
- const fileReader = new FileReader();
2138
- return new Promise((resolve, reject) => {
2139
- fileReader.onerror = () => {
2140
- reject(new DOMException('Problem reading blob.'));
2141
- };
2142
- fileReader.onload = () => {
2143
- resolve(fileReader.result);
2144
- };
2145
- fileReader.readAsDataURL(image);
2146
- });
2147
- }
2148
- function rescale(r, n) {
2149
- return r * n;
2150
- }
1855
+ /**
1856
+ * Add a field to a template.
1857
+ */
1858
+ const createField = (endpoint, templateId, params) => endpoint.api //
1859
+ .post(`/templates/${templateId}/fields`, params)
1860
+ .then((r) => r.data);
1861
+ /**
1862
+ * Update a template field.
1863
+ */
1864
+ const updateField = (endpoint, templateId, fieldName, params) => endpoint.api //
1865
+ .put(`/templates/${templateId}/fields/${fieldName}`, params)
1866
+ .then((r) => r.data);
1867
+ /**
1868
+ * REmove a field from a template.
1869
+ */
1870
+ const deleteField = (endpoint, templateId, fieldName) => endpoint.api //
1871
+ .delete(`/templates/${templateId}/fields/${fieldName}`)
1872
+ .then((r) => r.data);
2151
1873
 
2152
1874
  /**
2153
- * Given a File, extract the file's content as a base64 encoded data URL. The response will have a prefix that
2154
- * includes the MIME type of the file, e.g. "data:image/jpeg;base64,iVBORw0K......"
1875
+ * Enable automatic reminders. setup_time is the number of days after the envelope is sent that the first reminder
1876
+ * should be sent. interval_time is the number of days between reminders.
2155
1877
  */
2156
- const fileToDataUrl = (file) => new Promise((resolve, reject) => {
2157
- const reader = new FileReader();
2158
- reader.onload = () => resolve({
2159
- lastModified: file.lastModified,
2160
- size: file.size,
2161
- type: file.type,
2162
- name: file.name,
2163
- data: reader.result,
2164
- });
2165
- reader.onerror = reject;
2166
- if (file) {
2167
- reader.readAsDataURL(file);
2168
- }
2169
- else {
2170
- reject(new Error('Invalid file'));
2171
- }
2172
- });
1878
+ const createTemplateReminder = (endpoint, templateId, params) => endpoint.api //
1879
+ .post(`/templates/${templateId}/reminder/`, params)
1880
+ .then((r) => r.data);
2173
1881
  /**
2174
- * Trigger a download dialog to save a blob as a file on disk.
1882
+ * Get the reminder configuration for a template.
2175
1883
  */
2176
- const downloadBlob = (blob, name = 'file.pdf') => {
2177
- const blobUrl = URL.createObjectURL(blob);
2178
- const link = document.createElement('a');
2179
- link.href = blobUrl;
2180
- link.download = name;
2181
- document.body.appendChild(link);
2182
- link.dispatchEvent(new MouseEvent('click', {
2183
- bubbles: true,
2184
- cancelable: true,
2185
- view: window,
2186
- }));
2187
- document.body.removeChild(link);
2188
- };
1884
+ const getTemplateReminder = (endpoint, templateId, reminderId) => endpoint.api //
1885
+ .get(`/templates/${templateId}/reminder/${reminderId}`)
1886
+ .then((r) => r.data);
1887
+ /**
1888
+ * Update the reminder configuration for a template.
1889
+ */
1890
+ const updateTemplateReminder = (endpoint, templateId, reminderId, params) => endpoint.api //
1891
+ .put(`/templates/${templateId}/reminder/${reminderId}`, params)
1892
+ .then((r) => r.data);
1893
+ /**
1894
+ * Delete the reminder configuration for a template.
1895
+ */
1896
+ const deleteTemplateReminder = (endpoint, templateId, reminderId) => endpoint.api //
1897
+ .delete(`/templates/${templateId}/reminder/${reminderId}`)
1898
+ .then((r) => r.data);
1899
+
1900
+ /**
1901
+ * A "role" is an individual participant in a signing flow, such as a signer or CC contact. Roles are identified by
1902
+ * their names, which must be unique (e.g. 'Recipient 1'). Template fields are assigned to roles for signing operations,
1903
+ * so you may have 'Recipient 1 Signature 1' and so forth.
1904
+ *
1905
+ * @module
1906
+ */
1907
+ const createTemplateRole = (endpoint, templateId, params) => endpoint.api //
1908
+ .post(`/templates/${templateId}/roles`, params)
1909
+ .then((r) => r.data);
1910
+ const getTemplateRoles = (endpoint, templateId) => endpoint.api //
1911
+ .get(`/templates/${templateId}/roles`)
1912
+ .then((r) => r.data);
1913
+ const getTemplateRole = (endpoint, templateId, roleName) => endpoint.api //
1914
+ .get(`/templates/${templateId}/roles/${roleName}`)
1915
+ .then((r) => r.data);
1916
+ const updateTemplateRole = (endpoint, templateId, roleName, params) => endpoint.api //
1917
+ .put(`/templates/${templateId}/roles/${roleName}`, params)
1918
+ .then((r) => r.data);
1919
+ const deleteTemplateRole = (endpoint, templateId, roleName) => endpoint.api //
1920
+ .delete(`/templates/${templateId}/roles/${roleName}`)
1921
+ .then((r) => r.data);
1922
+ const getTemplateRoleFields = (endpoint, templateId, roleName) => endpoint.api //
1923
+ .get(`/templates/${templateId}/roles/${roleName}/fields`)
1924
+ .then((r) => r.data);
1925
+
1926
+ /**
1927
+ * Get the template stars for a template.
1928
+ */
1929
+ const getStars = (endpoint, templateId) => endpoint.api //
1930
+ .get(`/templates/${templateId}/stars`)
1931
+ .then((r) => r.data);
1932
+ /**
1933
+ * Toggle the template star for a template.
1934
+ */
1935
+ const toggleStar = (endpoint, templateId) => endpoint.api //
1936
+ .post(`/templates/${templateId}/stars/toggle`)
1937
+ .then((r) => r.data);
1938
+
1939
+ /**
1940
+ * A Tag is a user-specified label applied to a template. Tags help users organize and find Templates.
1941
+ * recipients. Every Organization has a set of tags "owned" by that Organization and only visible inside it.
1942
+ * Verdocs also provides a set of system-wide "featured" tags available to all Organizations.
1943
+ *
1944
+ * @module
1945
+ */
1946
+ /**
1947
+ * Apply a tag to a template.
1948
+ */
1949
+ const addTemplateTag = (endpoint, templateId, params) => endpoint.api //
1950
+ .post(`/templates/${templateId}/tags/`, params)
1951
+ .then((r) => r.data);
1952
+ /**
1953
+ * Get all tags for a template.
1954
+ */
1955
+ const getTemplateTags = (endpoint, templateId) => endpoint.api //
1956
+ .get(`/templates/${templateId}/tags/`)
1957
+ .then((r) => r.data);
1958
+ /**
1959
+ * Remove a tag from a template.
1960
+ */
1961
+ const deleteTemplateTag = (endpoint, templateId, tagName) => endpoint.api //
1962
+ .post(`/templates/${templateId}/tags/${tagName}`)
1963
+ .then((r) => r.data);
1964
+ /**
1965
+ * Create an Organization-wide tag.
1966
+ */
1967
+ const createTag = (endpoint, name) => endpoint.api //
1968
+ .post('/tags', { tag_name: name })
1969
+ .then((r) => r.data);
1970
+ /**
1971
+ * Get an Organization-wide tag.
1972
+ */
1973
+ const getTag = (endpoint, name) => endpoint.api //
1974
+ .get(`/tags/${name}`)
1975
+ .then((r) => r.data);
1976
+ /**
1977
+ * Get all tags available for use by an Organization.
1978
+ */
1979
+ const getAllTags = (endpoint) => endpoint.api //
1980
+ .get('/tags')
1981
+ .then((r) => r.data);
2189
1982
 
2190
- const Countries = [
2191
- { code: '+7 840', name: 'Abkhazia', value: '+7' },
2192
- { code: '+93', name: 'Afghanistan', value: '+93' },
2193
- { code: '+355', name: 'Albania', value: '+355' },
2194
- { code: '+213', name: 'Algeria', value: '+213' },
2195
- { code: '+1', name: 'American Samoa', value: '+1' },
2196
- { code: '+376', name: 'Andorra', value: '+376' },
2197
- { code: '+244', name: 'Angola', value: '+244' },
2198
- { code: '+1', name: 'Anguilla', value: '+1' },
2199
- { code: '+1', name: 'Antigua and Barbuda', value: '+1' },
2200
- { code: '+54', name: 'Argentina', value: '+54' },
2201
- { code: '+374', name: 'Armenia', value: '+374' },
2202
- { code: '+297', name: 'Aruba', value: '+297' },
2203
- { code: '+247', name: 'Ascension', value: '+247' },
2204
- { code: '+61', name: 'Australia', value: '+61' },
2205
- { code: '+672', name: 'Australian External Territories', value: '+672' },
2206
- { code: '+43', name: 'Austria', value: '+43' },
2207
- { code: '+994', name: 'Azerbaijan', value: '+994' },
2208
- { code: '+1', name: 'Bahamas', value: '+1' },
2209
- { code: '+973', name: 'Bahrain', value: '+973' },
2210
- { code: '+880', name: 'Bangladesh', value: '+880' },
2211
- { code: '+1', name: 'Barbados', value: '+1' },
2212
- { code: '+1', name: 'Barbuda', value: '+1' },
2213
- { code: '+375', name: 'Belarus', value: '+375' },
2214
- { code: '+32', name: 'Belgium', value: '+32' },
2215
- { code: '+501', name: 'Belize', value: '+501' },
2216
- { code: '+229', name: 'Benin', value: '+229' },
2217
- { code: '+1', name: 'Bermuda', value: '+1' },
2218
- { code: '+975', name: 'Bhutan', value: '+975' },
2219
- { code: '+591', name: 'Bolivia', value: '+591' },
2220
- { code: '+387', name: 'Bosnia and Herzegovina', value: '+387' },
2221
- { code: '+267', name: 'Botswana', value: '+267' },
2222
- { code: '+55', name: 'Brazil', value: '+55' },
2223
- { code: '+246', name: 'British Indian Ocean Territory', value: '+246' },
2224
- { code: '+1', name: 'British Virgin Islands', value: '+1' },
2225
- { code: '+673', name: 'Brunei', value: '+673' },
2226
- { code: '+359', name: 'Bulgaria', value: '+359' },
2227
- { code: '+226', name: 'Burkina Faso', value: '+226' },
2228
- { code: '+257', name: 'Burundi', value: '+257' },
2229
- { code: '+855', name: 'Cambodia', value: '+855' },
2230
- { code: '+237', name: 'Cameroon', value: '+237' },
2231
- { code: '+1', name: 'Canada', value: '+1' },
2232
- { code: '+238', name: 'Cape Verde', value: '+238' },
2233
- { code: '+1', name: 'Cayman Islands', value: '+1' },
2234
- { code: '+236', name: 'Central African Republic', value: '+236' },
2235
- { code: '+235', name: 'Chad', value: '+235' },
2236
- { code: '+56', name: 'Chile', value: '+56' },
2237
- { code: '+86', name: 'China', value: '+86' },
2238
- { code: '+61', name: 'Christmas Island', value: '+61' },
2239
- { code: '+61', name: 'Cocos-Keeling Islands', value: '+61' },
2240
- { code: '+57', name: 'Colombia', value: '+57' },
2241
- { code: '+269', name: 'Comoros', value: '+269' },
2242
- { code: '+242', name: 'Congo', value: '+242' },
2243
- { code: '+243', name: 'Congo, Dem. Rep. of (Zaire)', value: '+243' },
2244
- { code: '+682', name: 'Cook Islands', value: '+682' },
2245
- { code: '+506', name: 'Costa Rica', value: '+506' },
2246
- { code: '+385', name: 'Croatia', value: '+385' },
2247
- { code: '+53', name: 'Cuba', value: '+53' },
2248
- { code: '+599', name: 'Curacao', value: '+599' },
2249
- { code: '+537', name: 'Cyprus', value: '+537' },
2250
- { code: '+420', name: 'Czech Republic', value: '+420' },
2251
- { code: '+45', name: 'Denmark', value: '+45' },
2252
- { code: '+246', name: 'Diego Garcia', value: '+246' },
2253
- { code: '+253', name: 'Djibouti', value: '+253' },
2254
- { code: '+1', name: 'Dominica', value: '+1' },
2255
- { code: '+1', name: 'Dominican Republic', value: '+1' },
2256
- { code: '+670', name: 'East Timor', value: '+670' },
2257
- { code: '+56', name: 'Easter Island', value: '+56' },
2258
- { code: '+593', name: 'Ecuador', value: '+593' },
2259
- { code: '+20', name: 'Egypt', value: '+20' },
2260
- { code: '+503', name: 'El Salvador', value: '+503' },
2261
- { code: '+240', name: 'Equatorial Guinea', value: '+240' },
2262
- { code: '+291', name: 'Eritrea', value: '+291' },
2263
- { code: '+372', name: 'Estonia', value: '+372' },
2264
- { code: '+251', name: 'Ethiopia', value: '+251' },
2265
- { code: '+500', name: 'Falkland Islands', value: '+500' },
2266
- { code: '+298', name: 'Faroe Islands', value: '+298' },
2267
- { code: '+679', name: 'Fiji', value: '+679' },
2268
- { code: '+358', name: 'Finland', value: '+358' },
2269
- { code: '+33', name: 'France', value: '+33' },
2270
- { code: '+596', name: 'Martinique', value: '+596' },
2271
- { code: '+594', name: 'French Guiana', value: '+594' },
2272
- { code: '+689', name: 'French Polynesia', value: '+689' },
2273
- { code: '+241', name: 'Gabon', value: '+241' },
2274
- { code: '+220', name: 'Gambia', value: '+220' },
2275
- { code: '+995', name: 'Georgia', value: '+995' },
2276
- { code: '+49', name: 'Germany', value: '+49' },
2277
- { code: '+233', name: 'Ghana', value: '+233' },
2278
- { code: '+350', name: 'Gibraltar', value: '+350' },
2279
- { code: '+30', name: 'Greece', value: '+30' },
2280
- { code: '+299', name: 'Greenland', value: '+299' },
2281
- { code: '+1', name: 'Grenada', value: '+1' },
2282
- { code: '+590', name: 'Guadeloupe', value: '+590' },
2283
- { code: '+1', name: 'Guam', value: '+1' },
2284
- { code: '+502', name: 'Guatemala', value: '+502' },
2285
- { code: '+224', name: 'Guinea', value: '+224' },
2286
- { code: '+245', name: 'Guinea-Bissau', value: '+245' },
2287
- { code: '+595', name: 'Guyana', value: '+595' },
2288
- { code: '+509', name: 'Haiti', value: '+509' },
2289
- { code: '+504', name: 'Honduras', value: '+504' },
2290
- { code: '+852', name: 'Hong Kong SAR China', value: '+852' },
2291
- { code: '+36', name: 'Hungary', value: '+36' },
2292
- { code: '+354', name: 'Iceland', value: '+354' },
2293
- { code: '+91', name: 'India', value: '+91' },
2294
- { code: '+62', name: 'Indonesia', value: '+62' },
2295
- { code: '+98', name: 'Iran', value: '+98' },
2296
- { code: '+964', name: 'Iraq', value: '+964' },
2297
- { code: '+353', name: 'Ireland', value: '+353' },
2298
- { code: '+972', name: 'Israel', value: '+972' },
2299
- { code: '+39', name: 'Italy', value: '+39' },
2300
- { code: '+225', name: 'Ivory Coast', value: '+225' },
2301
- { code: '+1', name: 'Jamaica', value: '+1' },
2302
- { code: '+81', name: 'Japan', value: '+81' },
2303
- { code: '+962', name: 'Jordan', value: '+962' },
2304
- { code: '+77', name: 'Kazakhstan', value: '+7' },
2305
- { code: '+254', name: 'Kenya', value: '+254' },
2306
- { code: '+686', name: 'Kiribati', value: '+686' },
2307
- { code: '+965', name: 'Kuwait', value: '+965' },
2308
- { code: '+996', name: 'Kyrgyzstan', value: '+996' },
2309
- { code: '+856', name: 'Laos', value: '+856' },
2310
- { code: '+371', name: 'Latvia', value: '+371' },
2311
- { code: '+961', name: 'Lebanon', value: '+961' },
2312
- { code: '+266', name: 'Lesotho', value: '+266' },
2313
- { code: '+231', name: 'Liberia', value: '+231' },
2314
- { code: '+218', name: 'Libya', value: '+218' },
2315
- { code: '+423', name: 'Liechtenstein', value: '+423' },
2316
- { code: '+370', name: 'Lithuania', value: '+370' },
2317
- { code: '+352', name: 'Luxembourg', value: '+352' },
2318
- { code: '+853', name: 'Macau SAR China', value: '+853' },
2319
- { code: '+389', name: 'Macedonia', value: '+389' },
2320
- { code: '+261', name: 'Madagascar', value: '+261' },
2321
- { code: '+265', name: 'Malawi', value: '+265' },
2322
- { code: '+60', name: 'Malaysia', value: '+60' },
2323
- { code: '+960', name: 'Maldives', value: '+960' },
2324
- { code: '+223', name: 'Mali', value: '+223' },
2325
- { code: '+356', name: 'Malta', value: '+356' },
2326
- { code: '+692', name: 'Marshall Islands', value: '+692' },
2327
- { code: '+596', name: 'Martinique', value: '+596' },
2328
- { code: '+222', name: 'Mauritania', value: '+222' },
2329
- { code: '+230', name: 'Mauritius', value: '+230' },
2330
- { code: '+262', name: 'Mayotte or Réunion', value: '+262' },
2331
- { code: '+52', name: 'Mexico', value: '+52' },
2332
- { code: '+691', name: 'Micronesia', value: '+691' },
2333
- { code: '+1', name: 'Midway Island', value: '+1' },
2334
- { code: '+373', name: 'Moldova', value: '+373' },
2335
- { code: '+377', name: 'Monaco', value: '+377' },
2336
- { code: '+976', name: 'Mongolia', value: '+976' },
2337
- { code: '+382', name: 'Montenegro', value: '+382' },
2338
- { code: '+1', name: 'Montserrat', value: '+1' },
2339
- { code: '+212', name: 'Morocco', value: '+212' },
2340
- { code: '+95', name: 'Myanmar', value: '+95' },
2341
- { code: '+264', name: 'Namibia', value: '+264' },
2342
- { code: '+674', name: 'Nauru', value: '+674' },
2343
- { code: '+977', name: 'Nepal', value: '+977' },
2344
- { code: '+31', name: 'Netherlands', value: '+31' },
2345
- { code: '+599', name: 'Netherlands Antilles', value: '+599' },
2346
- { code: '+1', name: 'Nevis', value: '+1' },
2347
- { code: '+687', name: 'New Caledonia', value: '+687' },
2348
- { code: '+64', name: 'New Zealand', value: '+64' },
2349
- { code: '+505', name: 'Nicaragua', value: '+505' },
2350
- { code: '+227', name: 'Niger', value: '+227' },
2351
- { code: '+234', name: 'Nigeria', value: '+234' },
2352
- { code: '+683', name: 'Niue', value: '+683' },
2353
- { code: '+672', name: 'Norfolk Island', value: '+672' },
2354
- { code: '+850', name: 'North Korea', value: '+850' },
2355
- { code: '+1', name: 'Northern Mariana Islands', value: '+1' },
2356
- { code: '+47', name: 'Norway', value: '+47' },
2357
- { code: '+968', name: 'Oman', value: '+968' },
2358
- { code: '+92', name: 'Pakistan', value: '+92' },
2359
- { code: '+680', name: 'Palau', value: '+680' },
2360
- { code: '+970', name: 'Palestinian Territory', value: '+970' },
2361
- { code: '+507', name: 'Panama', value: '+507' },
2362
- { code: '+675', name: 'Papua New Guinea', value: '+675' },
2363
- { code: '+595', name: 'Paraguay', value: '+595' },
2364
- { code: '+51', name: 'Peru', value: '+51' },
2365
- { code: '+63', name: 'Philippines', value: '+63' },
2366
- { code: '+48', name: 'Poland', value: '+48' },
2367
- { code: '+351', name: 'Portugal', value: '+351' },
2368
- { code: '+1', name: 'Puerto Rico', value: '+1' },
2369
- { code: '+974', name: 'Qatar', value: '+974' },
2370
- { code: '+40', name: 'Romania', value: '+40' },
2371
- { code: '+7', name: 'Russia', value: '+7' },
2372
- { code: '+250', name: 'Rwanda', value: '+250' },
2373
- { code: '508', name: 'Saint Pierre and Miquelon', value: '508' },
2374
- { code: '+685', name: 'Samoa', value: '+685' },
2375
- { code: '+378', name: 'San Marino', value: '+378' },
2376
- { code: '+966', name: 'Saudi Arabia', value: '+966' },
2377
- { code: '+221', name: 'Senegal', value: '+221' },
2378
- { code: '+381', name: 'Serbia', value: '+381' },
2379
- { code: '+248', name: 'Seychelles', value: '+248' },
2380
- { code: '+232', name: 'Sierra Leone', value: '+232' },
2381
- { code: '+65', name: 'Singapore', value: '+65' },
2382
- { code: '+421', name: 'Slovakia', value: '+421' },
2383
- { code: '+386', name: 'Slovenia', value: '+386' },
2384
- { code: '+677', name: 'Solomon Islands', value: '+677' },
2385
- { code: '+27', name: 'South Africa', value: '+27' },
2386
- { code: '+500', name: 'South Georgia and the South Sandwich Islands', value: '+500' },
2387
- { code: '+82', name: 'South Korea', value: '+82' },
2388
- { code: '+34', name: 'Spain', value: '+34' },
2389
- { code: '+94', name: 'Sri Lanka', value: '+94' },
2390
- { code: '+249', name: 'Sudan', value: '+249' },
2391
- { code: '+597', name: 'Suriname', value: '+597' },
2392
- { code: '+268', name: 'Swaziland', value: '+268' },
2393
- { code: '+46', name: 'Sweden', value: '+46' },
2394
- { code: '+41', name: 'Switzerland', value: '+41' },
2395
- { code: '+963', name: 'Syria', value: '+963' },
2396
- { code: '+886', name: 'Taiwan', value: '+886' },
2397
- { code: '+992', name: 'Tajikistan', value: '+992' },
2398
- { code: '+255', name: 'Tanzania', value: '+255' },
2399
- { code: '+66', name: 'Thailand', value: '+66' },
2400
- { code: '+670', name: 'Timor Leste', value: '+670' },
2401
- { code: '+228', name: 'Togo', value: '+228' },
2402
- { code: '+690', name: 'Tokelau', value: '+690' },
2403
- { code: '+676', name: 'Tonga', value: '+676' },
2404
- { code: '+1', name: 'Trinidad and Tobago', value: '+1' },
2405
- { code: '+216', name: 'Tunisia', value: '+216' },
2406
- { code: '+90', name: 'Turkey', value: '+90' },
2407
- { code: '+993', name: 'Turkmenistan', value: '+993' },
2408
- { code: '+1', name: 'Turks and Caicos Islands', value: '+1' },
2409
- { code: '+688', name: 'Tuvalu', value: '+688' },
2410
- { code: '+1', name: 'U.S. Virgin Islands', value: '+1' },
2411
- { code: '+256', name: 'Uganda', value: '+256' },
2412
- { code: '+380', name: 'Ukraine', value: '+380' },
2413
- { code: '+971', name: 'United Arab Emirates', value: '+971' },
2414
- { code: '+44', name: 'United Kingdom', value: '+44' },
2415
- { code: '+1', name: 'United States', value: '+1' },
2416
- { code: '+598', name: 'Uruguay', value: '+598' },
2417
- { code: '+998', name: 'Uzbekistan', value: '+998' },
2418
- { code: '+678', name: 'Vanuatu', value: '+678' },
2419
- { code: '+58', name: 'Venezuela', value: '+58' },
2420
- { code: '+84', name: 'Vietnam', value: '+84' },
2421
- { code: '+1', name: 'Wake Island', value: '+1' },
2422
- { code: '+681', name: 'Wallis and Futuna', value: '+681' },
2423
- { code: '+967', name: 'Yemen', value: '+967' },
2424
- { code: '+260', name: 'Zambia', value: '+260' },
2425
- { code: '+255', name: 'Zanzibar', value: '+255' },
2426
- { code: '+263', name: 'Zimbabwe', value: '+263' },
1983
+ /**
1984
+ * A Template defines how a Verdocs signing flow will be performed, including attachments, signing fields, and
1985
+ * recipients.
1986
+ *
1987
+ * @module
1988
+ */
1989
+ /**
1990
+ * Get all templates accessible by the caller, with optional filters.
1991
+ *
1992
+ * ```typescript
1993
+ * import {Templates} from '@verdocs/js-sdk/Templates';
1994
+ *
1995
+ * await Templates.getTemplates((VerdocsEndpoint.getDefault());
1996
+ * await Templates.getTemplates((VerdocsEndpoint.getDefault(), { is_starred: true });
1997
+ * await Templates.getTemplates((VerdocsEndpoint.getDefault(), { is_creator: true });
1998
+ * await Templates.getTemplates((VerdocsEndpoint.getDefault(), { is_organization: true });
1999
+ * ```
2000
+ */
2001
+ const getTemplates = (endpoint, params) => endpoint.api //
2002
+ .post('/templates', { params })
2003
+ .then((r) => r.data);
2004
+ // export interface IListTemplatesParams {
2005
+ // name?: string;
2006
+ // sharing?: 'all' | 'personal' | 'shared' | 'public';
2007
+ // starred?: 'all' | 'starred' | 'unstarred';
2008
+ // sort?: 'name' | 'created_at' | 'updated_at' | 'last_used_at' | 'counter' | 'star_counter';
2009
+ // direction?: 'asc' | 'desc';
2010
+ // page?: number;
2011
+ // rows?: number;
2012
+ // }
2013
+ /**
2014
+ * Lists all templates accessible by the caller, with optional filters.
2015
+ *
2016
+ * ```typescript
2017
+ * import {Templates} from '@verdocs/js-sdk/Templates';
2018
+ *
2019
+ * await Templates.listTemplates((VerdocsEndpoint.getDefault(), { sharing: 'personal', sort: 'last_used_at' });
2020
+ * ```
2021
+ */
2022
+ // export const listTemplates = (endpoint: VerdocsEndpoint, params?: IListTemplatesParams) =>
2023
+ // endpoint.api //
2024
+ // .post<ITemplateSummaries>('/templates/list', params, {baseURL: endpoint.getBaseURLv2()})
2025
+ // .then((r) => r.data);
2026
+ /**
2027
+ * Get one template by its ID.
2028
+ *
2029
+ * ```typescript
2030
+ * import {Templates} from '@verdocs/js-sdk/Templates';
2031
+ *
2032
+ * const template = await Templates.getTemplate((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9');
2033
+ * ```
2034
+ */
2035
+ const getTemplate = (endpoint, templateId) => endpoint.api //
2036
+ .get(`/templates/${templateId}`)
2037
+ .then((r) => r.data);
2038
+ /**
2039
+ * Get owner information for a template.
2040
+ *
2041
+ * ```typescript
2042
+ * import {Templates} from '@verdocs/js-sdk/Templates';
2043
+ *
2044
+ * const template = await Templates.getTemplateOwnerInfo((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9');
2045
+ * ```
2046
+ */
2047
+ const getTemplateOwnerInfo = (endpoint, templateId) => endpoint.api //
2048
+ .get(`/templates/${templateId}`)
2049
+ .then((r) => r.data);
2050
+ const ALLOWED_CREATE_FIELDS = [
2051
+ 'name',
2052
+ 'is_personal',
2053
+ 'is_public',
2054
+ 'sender',
2055
+ 'description',
2056
+ 'roles',
2057
+ 'fields',
2427
2058
  ];
2428
- function getCountryByCode(code) {
2429
- const found = Countries.find((country) => country.code === code);
2430
- if (found)
2431
- return found;
2432
- if (isFrenchGuiana(code)) {
2433
- return { code: '+594', name: 'French Guiana', value: '+594' };
2059
+ /**
2060
+ * Create a template.
2061
+ *
2062
+ * ```typescript
2063
+ * import {Templates} from '@verdocs/js-sdk/Templates';
2064
+ *
2065
+ * const newTemplate = await Templates.createTemplate((VerdocsEndpoint.getDefault(), {...});
2066
+ * ```
2067
+ */
2068
+ const createTemplate = (endpoint, params, onUploadProgress) => {
2069
+ const options = {
2070
+ timeout: 120000,
2071
+ onUploadProgress: (event) => {
2072
+ const total = event.total || 1;
2073
+ const loaded = event.loaded || 0;
2074
+ onUploadProgress?.(Math.floor((loaded * 100) / (total || 1)), loaded, total || 1);
2075
+ },
2076
+ };
2077
+ if (params.documents && params.documents[0] instanceof File) {
2078
+ if (params.documents.length > 10) {
2079
+ throw new Error('createTemplate() has a maximum of 10 documents that can be attached.');
2080
+ }
2081
+ const formData = new FormData();
2082
+ ALLOWED_CREATE_FIELDS.forEach((allowedKey) => {
2083
+ if (params[allowedKey] !== undefined) {
2084
+ formData.append(allowedKey, params[allowedKey]);
2085
+ }
2086
+ });
2087
+ params.documents.forEach((file) => {
2088
+ formData.append('documents', file, file.name);
2089
+ });
2090
+ return endpoint.api.post('/templates', formData, options).then((r) => r.data);
2434
2091
  }
2435
- else if (isGuadeloupe(code)) {
2436
- return { code: '+590', name: 'Guadeloupe', value: '+590' };
2092
+ else {
2093
+ return endpoint.api.post('/templates', params, options).then((r) => r.data);
2437
2094
  }
2438
- else if (isMartinique(code)) {
2439
- return { code: '+596', name: 'Martinique', value: '+596' };
2095
+ };
2096
+ /**
2097
+ * Create a template.
2098
+ *
2099
+ * ```typescript
2100
+ * import {Templates} from '@verdocs/js-sdk/Templates';
2101
+ *
2102
+ * const newTemplate = await Templates.createTemplatev2((VerdocsEndpoint.getDefault(), {...});
2103
+ * ```
2104
+ */
2105
+ const createTemplatev2 = (endpoint, params, onUploadProgress) => {
2106
+ const options = {
2107
+ timeout: 120000,
2108
+ onUploadProgress: (event) => {
2109
+ const total = event.total || 1;
2110
+ const loaded = event.loaded || 0;
2111
+ onUploadProgress?.(Math.floor((loaded * 100) / (total || 1)), loaded, total || 1);
2112
+ },
2113
+ };
2114
+ if (params.documents && params.documents[0] instanceof File) {
2115
+ const formData = new FormData();
2116
+ ALLOWED_CREATE_FIELDS.forEach((allowedKey) => {
2117
+ if (params[allowedKey] !== undefined) {
2118
+ formData.append(allowedKey, params[allowedKey]);
2119
+ }
2120
+ });
2121
+ params.documents.forEach((file) => {
2122
+ formData.append('documents', file, file.name);
2123
+ });
2124
+ return endpoint.api.post('/v2/templates', formData, options).then((r) => r.data);
2440
2125
  }
2441
- else if (isMayotte(code)) {
2442
- return { code: '+262', name: 'Mayotte or Réunion', value: '+262' };
2126
+ else {
2127
+ return endpoint.api.post('/v2/templates', params, options).then((r) => r.data);
2443
2128
  }
2444
- return null;
2445
- }
2446
- function isFrenchGuiana(code) {
2447
- return '+594' === code.substring(0, 4);
2448
- }
2449
- function isGuadeloupe(code) {
2450
- return '+590' === code.substring(0, 4);
2451
- }
2452
- function isMartinique(code) {
2453
- return '+596' === code.substring(0, 4);
2454
- }
2455
- function isMayotte(code) {
2456
- return '+262' === code.substring(0, 4);
2457
- }
2458
- function getPlusOneCountry(code) {
2459
- let info = null;
2460
- switch (code.substring(0, 5)) {
2461
- case '+1684':
2462
- info = { code: '+1', name: 'American Samoa', value: '+1' };
2463
- break;
2464
- case '+1264':
2465
- info = { code: '+1', name: 'Anguilla', value: '+1' };
2466
- break;
2467
- case '+1268':
2468
- info = { code: '+1', name: 'Antigua and Barbuda', value: '+1' };
2469
- break;
2470
- case '+1242':
2471
- info = { code: '+1', name: 'Bahamas', value: '+1' };
2472
- break;
2473
- case '+1246':
2474
- info = { code: '+1', name: 'Barbados', value: '+1' };
2475
- break;
2476
- case '+1441':
2477
- info = { code: '+1', name: 'Bermuda', value: '+1' };
2478
- break;
2479
- case '+1284':
2480
- info = { code: '+1', name: 'British Virgin Islands', value: '+1' };
2481
- break;
2482
- case '+1':
2483
- info = { code: '+1', name: '', value: '+1' };
2484
- break;
2129
+ };
2130
+ /**
2131
+ * Create a template from a Sharepoint asset.
2132
+ *
2133
+ * ```typescript
2134
+ * import {Templates} from '@verdocs/js-sdk/Templates';
2135
+ *
2136
+ * const newTemplate = await Templates.createTemplateFromSharepoint((VerdocsEndpoint.getDefault(), {...});
2137
+ * ```
2138
+ */
2139
+ const createTemplateFromSharepoint = (endpoint, params) => {
2140
+ const options = {
2141
+ timeout: 120000,
2142
+ };
2143
+ return endpoint.api.post('/templates/from-sharepoint', params, options).then((r) => r.data);
2144
+ };
2145
+ /**
2146
+ * Update a template.
2147
+ *
2148
+ * ```typescript
2149
+ * import {Templates} from '@verdocs/js-sdk/Templates';
2150
+ *
2151
+ * const updatedTemplate = await Templates.updateTemplate((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9', { name: 'New Name' });
2152
+ * ```
2153
+ */
2154
+ const updateTemplate = (endpoint, templateId, params) => endpoint.api //
2155
+ .put(`/templates/${templateId}`, params)
2156
+ .then((r) => r.data);
2157
+ /**
2158
+ * Delete a template.
2159
+ *
2160
+ * ```typescript
2161
+ * import {Templates} from '@verdocs/js-sdk/Templates';
2162
+ *
2163
+ * await Templates.deleteTemplate((VerdocsEndpoint.getDefault(), '83da3d70-7857-4392-b876-c4592a304bc9');
2164
+ * ```
2165
+ */
2166
+ const deleteTemplate = (endpoint, templateId) => endpoint.api //
2167
+ .delete(`/templates/${templateId}`)
2168
+ .then((r) => r.data);
2169
+ /**
2170
+ * Search for templates matching various criteria.
2171
+ *
2172
+ * ```typescript
2173
+ * import {Templates} from '@verdocs/js-sdk/Templates';
2174
+ *
2175
+ * const {result, page, total} = await Templates.search((VerdocsEndpoint.getDefault(), { ... });
2176
+ * ```
2177
+ */
2178
+ const searchTemplates = async (endpoint, params) => endpoint.api //
2179
+ .post('/templates/search', params)
2180
+ .then((r) => r.data);
2181
+ /**
2182
+ * Get a summary of template data, typically used to populate admin panel dashboard pages.
2183
+ *
2184
+ * ```typescript
2185
+ * import {Templates} from '@verdocs/js-sdk/Templates';
2186
+ *
2187
+ * const summary = await Templates.getSummary((VerdocsEndpoint.getDefault(), 0);
2188
+ * ```
2189
+ */
2190
+ const getTemplatesSummary = async (endpoint, params = {}) => endpoint.api //
2191
+ .post('/templates/summary', params)
2192
+ .then((r) => r.data);
2193
+ const cachedTemplates = {};
2194
+ /**
2195
+ * Wrapper for `getTemplate()` that limits queries to one every 2 seconds per template ID.
2196
+ * This is intended for use in component hierarchies that all rely on the same template
2197
+ * to avoid unnecessary repeat server calls.
2198
+ */
2199
+ const throttledGetTemplate = (endpoint, templateId) => {
2200
+ if (cachedTemplates[templateId] && cachedTemplates[templateId].loaded + 2000 < new Date().getTime()) {
2201
+ return cachedTemplates[templateId].template;
2485
2202
  }
2486
- return info;
2487
- }
2488
- function isCanada(code) {
2489
- const canadianAreaCodes = [
2490
- '403',
2491
- '587',
2492
- '780',
2493
- '825',
2494
- '604',
2495
- '250',
2496
- '778',
2497
- '236',
2498
- '204',
2499
- '431',
2500
- '506',
2501
- '709',
2502
- '867',
2503
- '782',
2504
- '902',
2505
- '867',
2506
- '548',
2507
- '705',
2508
- '365',
2509
- '613',
2510
- '807',
2511
- '226',
2512
- '289',
2513
- '437',
2514
- '519',
2515
- '647',
2516
- '905',
2517
- '249',
2518
- '343',
2519
- '416',
2520
- '902',
2521
- '782',
2522
- '450',
2523
- '418',
2524
- '579',
2525
- '873',
2526
- '367',
2527
- '514',
2528
- '581',
2529
- '819',
2530
- '438',
2531
- '639',
2532
- '306',
2533
- '867',
2534
- ];
2535
- const areaCode = code.substring(0, 5);
2536
- return canadianAreaCodes.findIndex((x) => '+1' + x === areaCode) > -1;
2537
- }
2538
- function isAmericanSamoa(code) {
2539
- return code.substring(0, 5) === '+1684';
2540
- }
2541
- function isDominicanRepublic(code) {
2542
- return '+1809' === code.substring(0, 5) || '+1829' === code.substring(0, 5) || '+1849' === code.substring(0, 5);
2543
- }
2544
- function isPuertoRico(code) {
2545
- return code.substring(0, 5) === '+' || code.substring(0, 5) === '+';
2546
- }
2547
- // need to finish
2548
- function getMatchingCountry(code, substrings) {
2549
- const toMatch = code.substring(0, substrings);
2550
- return Countries.filter((c) => c.code === toMatch).length;
2551
- }
2552
- // const e164Regex = new RegExp(/\+[1-9]\d{6,14}/g);
2553
- // export function simpleE164Validator(code: string) {
2554
- // return (code !== null && code.length < 16 && code.length > 6 && e164Regex.test(code)) || code === '' || code === null;
2555
- // }
2203
+ return getTemplate(endpoint, templateId).then((template) => {
2204
+ cachedTemplates[templateId] = { loaded: new Date().getTime(), template };
2205
+ return template;
2206
+ });
2207
+ };
2208
+ /**
2209
+ * List templates.
2210
+ *
2211
+ * ```typescript
2212
+ * import {Templates} from '@verdocs/js-sdk/Templates';
2213
+ *
2214
+ * const {totals, templates} = await Templates.listTemplates((VerdocsEndpoint.getDefault(), { q: 'test', sort: 'created_at' }); * ```
2215
+ */
2216
+ const listTemplates = async (endpoint, params = {}) => endpoint.api //
2217
+ .post('/templates/list', params)
2218
+ .then((r) => r.data);
2219
+
2220
+ /**
2221
+ * A TemplateDocument represents a PDF or other attachment in a Template.
2222
+ *
2223
+ * @module
2224
+ */
2225
+ /**
2226
+ * Get all the Template Documents associated to a particular Template.
2227
+ *
2228
+ * ```typescript
2229
+ * import {TemplateDocument} from '@verdocs/js-sdk/Templates';
2230
+ *
2231
+ * await TemplateDocument.geTemplateDocuments((VerdocsEndpoint.getDefault(), templateId);
2232
+ * ```
2233
+ */
2234
+ const getTemplateDocuments = (endpoint, templateId) => endpoint.api //
2235
+ .get(`/templates/${templateId}/documents/`)
2236
+ .then((r) => r.data);
2237
+ /**
2238
+ * Get a specific Document.
2239
+ *
2240
+ * ```typescript
2241
+ * import {TemplateDocument} from '@verdocs/js-sdk/Templates';
2242
+ *
2243
+ * await TemplateDocument.geTemplateDocument((VerdocsEndpoint.getDefault(), templateId,documentId);
2244
+ * ```
2245
+ */
2246
+ const getTemplateDocument = (endpoint, templateId, documentId) => endpoint.api //
2247
+ .get(`/templates/${templateId}/documents/${documentId}`)
2248
+ .then((r) => r.data);
2249
+ /**
2250
+ * Create a Document for a particular Template.
2251
+ *
2252
+ * ```typescript
2253
+ * import {TemplateDocument} from '@verdocs/js-sdk/Templates';
2254
+ *
2255
+ * await TemplateDocument.createDocument((VerdocsEndpoint.getDefault(), templateID, params);
2256
+ * ```
2257
+ */
2258
+ const createTemplateDocument = (endpoint, templateId, file, onUploadProgress) => {
2259
+ const formData = new FormData();
2260
+ formData.append('document', file, file.name);
2261
+ return endpoint.api //
2262
+ .post(`/templates/${templateId}/documents`, formData, {
2263
+ timeout: 120000,
2264
+ onUploadProgress: (event) => {
2265
+ const total = event.total || 1;
2266
+ const loaded = event.loaded || 0;
2267
+ onUploadProgress?.(Math.floor((loaded * 100) / (total || 1)), loaded, total || 1);
2268
+ },
2269
+ })
2270
+ .then((r) => r.data);
2271
+ };
2272
+ /**
2273
+ * Delete a specific Document.
2274
+ *
2275
+ * ```typescript
2276
+ * import {TemplateDocument} from '@verdocs/js-sdk/Templates';
2277
+ *
2278
+ * await TemplateDocument.deleteDocument((VerdocsEndpoint.getDefault(), templateID, documentID);
2279
+ * ```
2280
+ */
2281
+ const deleteTemplateDocument = (endpoint, templateId, documentId) => endpoint.api //
2282
+ .delete(`/templates/${templateId}/documents/${documentId}`)
2283
+ .then((r) => r.data);
2284
+ /**
2285
+ * Get (binary download) a file attached to a Template. It is important to use this method
2286
+ * rather than a direct A HREF or similar link to set the authorization headers for the
2287
+ * request.
2288
+ */
2289
+ const getTemplateDocumentFile = async (endpoint, templateId, documentId) => endpoint.api //
2290
+ .get(`/templates/${templateId}/documents/${documentId}?file=true`, { responseType: 'blob' })
2291
+ .then((r) => r.data);
2292
+ /**
2293
+ * Get (binary download) a file attached to a Template. It is important to use this method
2294
+ * rather than a direct A HREF or similar link to set the authorization headers for the
2295
+ * request.
2296
+ */
2297
+ const getTemplateDocumentThumbnail = async (endpoint, templateId, documentId) => endpoint.api //
2298
+ .get(`/templates/${templateId}/documents/${documentId}?thumbnail=true`, { responseType: 'blob' })
2299
+ .then((r) => r.data);
2300
+ /**
2301
+ * Get a display URI for a given page in a file attached to a template document. These pages are rendered server-side
2302
+ * into PNG resources suitable for display in IMG tags although they may be used elsewhere. Note that these are intended
2303
+ * for DISPLAY ONLY, are not legally binding documents, and do not contain any encoded metadata from participants. The
2304
+ * original asset may be obtained by calling `getTemplateDocumentFile()` or similar.
2305
+ */
2306
+ const getTemplateDocumentPageDisplayUri = async (endpoint, templateId, documentId, page) => endpoint.api.get(`/templates/${templateId}/documents/${documentId}/pages/${page}/image`).then((r) => r.data);
2307
+
2308
+ /**
2309
+ * Get all defined validators
2310
+ *
2311
+ * ```typescript
2312
+ * import {Documents} from '@verdocs/js-sdk/Templates';
2313
+ *
2314
+ * await Documents.getDocuments(templateID);
2315
+ * ```
2316
+ */
2317
+ const getValidators = (endpoint) => endpoint.api //
2318
+ .get('/validators')
2319
+ .then((r) => r.data);
2320
+ const getValidator = (endpoint, validatorName) => endpoint.api //
2321
+ .get(`/validators/${validatorName}`)
2322
+ .then((r) => r.data);
2323
+ const EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
2324
+ const isValidEmail = (email) => !!email && EMAIL_REGEX.test(email);
2325
+ // @see https://www.regextester.com/1978
2326
+ const PHONE_REGEX = /((?:\+|00)[17](?: |\-)?|(?:\+|00)[1-9]\d{0,2}(?: |\-)?|(?:\+|00)1\-\d{3}(?: |\-)?)?(0\d|\([0-9]{3}\)|[1-9]{0,3})(?:((?: |\-)[0-9]{2}){4}|((?:[0-9]{2}){4})|((?: |\-)[0-9]{3}(?: |\-)[0-9]{4})|([0-9]{7}))/;
2327
+ const isValidPhone = (phone) => !!phone && PHONE_REGEX.test(phone);
2328
+ const isValidRoleName = (value, roles) => roles.findIndex((role) => role.name === value) !== -1;
2329
+ const TagRegEx = /^[a-zA-Z0-9-]{0,32}$/;
2330
+ const isValidTag = (value, tags) => TagRegEx.test(value) || tags.findIndex((tag) => tag === value) !== -1;
2556
2331
 
2557
2332
  /**
2558
- * Capitalize the first letter of a string.
2333
+ * Authenticate to Verdocs via user/password authentication
2334
+ *
2335
+ * ```typescript
2336
+ * import {Auth} from '@verdocs/js-sdk/Auth';
2337
+ * import {Transport} from '@verdocs/js-sdk/HTTP';
2338
+ *
2339
+ * const {accessToken} = await Auth.authenticateUser({ username: 'test@test.com', password: 'PASSWORD' });
2340
+ * Transport.setAuthToken(accessToken);
2341
+ * ```
2342
+ */
2343
+ const authenticateUser = (endpoint, params) => endpoint.api //
2344
+ .post('/authentication/login', params)
2345
+ .then((r) => r.data);
2346
+ /**
2347
+ * Authenticate to Verdocs via client ID / Secret authentication. **NOTE: This is only suitable for
2348
+ * NodeJS server-side applications. Never expose your Client Secret in a Web or Mobile app!** Also note
2349
+ * that access tokens may be cached by server-side apps (and this is recommended) but do expire after 2
2350
+ * hours. This expiration may change based on future security needs. Application developers are encouraged
2351
+ * to check the `exp` expiration field in the response accessToken and renew tokens after they expire.
2352
+ *
2353
+ * ```typescript
2354
+ * import {Auth} from '@verdocs/js-sdk/Auth';
2355
+ * import {Transport} from '@verdocs/js-sdk/HTTP';
2356
+ *
2357
+ * const {accessToken} = await Auth.authenticateApp({ client_id: 'CLIENTID', client_secret: 'SECRET' });
2358
+ * Transport.setAuthToken(accessToken);
2359
+ * ```
2360
+ */
2361
+ const authenticateApp = (endpoint, params) => endpoint.api //
2362
+ .post('/authentication/login_client', {}, { headers: params })
2363
+ .then((r) => r.data);
2364
+ /**
2365
+ * Validate a token. Only Verdocs tokens will be accepted. Most applications can decode tokens locally,
2366
+ * because tokens will be validated when API calls are made anyway. However, high-security applications
2367
+ * may use this endpoint to check if a token has been revoked.
2368
+ *
2369
+ * ```typescript
2370
+ * import {Auth} from '@verdocs/js-sdk/Auth';
2371
+ *
2372
+ * const {valid} = await Auth.validateToken({ token });
2373
+ * if (!valid) {
2374
+ * window.alert('Session invalid or expired. Please re-authenticate.');
2375
+ * }
2376
+ * ```
2377
+ */
2378
+ const validateToken = (endpoint, params) => endpoint.api //
2379
+ .post('/token/isValid', params)
2380
+ .then((r) => r.data);
2381
+ /**
2382
+ * If called before the session expires, this will refresh the caller's session and tokens.
2383
+ *
2384
+ * ```typescript
2385
+ * import {Auth} from '@verdocs/js-sdk/Auth';
2386
+ * import {Transport} from '@verdocs/js-sdk/HTTP';
2387
+ *
2388
+ * const {accessToken} = await Auth.refreshTokens();
2389
+ * Transport.setAuthToken(accessToken);
2390
+ * ```
2391
+ */
2392
+ const refreshTokens = (endpoint) => endpoint.api //
2393
+ .get('/token')
2394
+ .then((r) => r.data);
2395
+ /**
2396
+ * Update the caller's password. To help prevent CSRF attack vectors, the user's old password and email address are required.
2397
+ *
2398
+ * ```typescript
2399
+ * import {Auth} from '@verdocs/js-sdk/Auth';
2400
+ *
2401
+ * const {status, message} = await Auth.updatePassword({ email, oldPassword, newPassword });
2402
+ * if (status !== 'OK') {
2403
+ * window.alert(`Password reset error: ${message}`);
2404
+ * }
2405
+ * ```
2559
2406
  */
2560
- const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
2407
+ const updatePassword = (endpoint, params) => endpoint.api //
2408
+ .put('/user/update_password', params)
2409
+ .then((r) => r.data);
2561
2410
  /**
2562
- * Convert a phone-number-like string to E164 format.
2563
- * @see https://46elks.com/kb/e164
2411
+ * Reset the caller's password.
2412
+ *
2413
+ * ```typescript
2414
+ * import {Auth} from '@verdocs/js-sdk/Auth';
2415
+ *
2416
+ * const {success} = await Auth.resetPassword({ email });
2417
+ * if (status !== 'OK') {
2418
+ * window.alert(`Please check your email for instructions on how to reset your password.`);
2419
+ * }
2420
+ * ```
2564
2421
  */
2565
- const convertToE164 = (input) => {
2566
- // "(212) 555-1212" => +12125551212
2567
- // "+46766861004" => "+46766861004"
2568
- // "212-555-1212" => +12125551212
2569
- // "212.555.1212" => +12125551212
2570
- // "212 555 1212" => +12125551212
2571
- let temp = (input || '').trim();
2572
- // If we are already prefixed, assume the user did it deliberately and attempt to use what they entered. We also short-circuit blanks.
2573
- if (!temp || temp.startsWith('+')) {
2574
- return temp;
2575
- }
2576
- // Remove any spaces, parenthesis or other punctuation.
2577
- temp = temp.replace(/[^0-9]/g, '');
2578
- // If the number begins with a zero, remove the leading zero. Do not combine this with the previous step because it needs to be removed
2579
- // whether it's the actual first character e.g. `0(5)` or just the first digit e.g. `(05`.
2580
- temp = temp.replace(/^0/g, '');
2581
- // Prepend the country code and +. We're assuming US in this case given the target demographic. Users in other countries would/should be
2582
- // already entering a prefix so they'd shortcut out of this routine via the + prefix check.
2583
- return `+1${temp}`;
2584
- };
2422
+ const resetPassword = (endpoint, params) => endpoint.api //
2423
+ .post('/user/reset_password', params)
2424
+ .then((r) => r.data);
2425
+ /**
2426
+ * Update the caller's email address.
2427
+ *
2428
+ * ```typescript
2429
+ * import {Auth} from '@verdocs/js-sdk/Auth';
2430
+ *
2431
+ * const {profiles} = await Auth.updateEmail({ email: newEmail });
2432
+ * ```
2433
+ */
2434
+ const updateEmail = (endpoint, params) => endpoint.api //
2435
+ .put('/user/update_email', params)
2436
+ .then((r) => r.data);
2437
+ /**
2438
+ * Resend the email verification request. Note that to prevent certain forms of abuse, the email address is not
2439
+ * a parameter here. Instead, the caller must be authenticated as the (unverified) user. To simplify this process,
2440
+ * the access token to be used may be passed directly as a parameter here. This avoids the need to set it as the
2441
+ * active token on an endpoint, which may be inconvenient in workflows where it is preferable to keep the user in
2442
+ * "anonymous" mode while verification is being performed.
2443
+ *
2444
+ * ```typescript
2445
+ * import {Auth} from '@verdocs/js-sdk/Auth';
2446
+ *
2447
+ * const result = await Auth.resendVerification();
2448
+ * ```
2449
+ */
2450
+ const resendVerification = (endpoint, accessToken) => endpoint.api //
2451
+ .post('/user/email_verification', {}, accessToken ? { headers: { Authorization: `Bearer ${accessToken}` } } : {})
2452
+ .then((r) => r.data);
2453
+ const createUser = (endpoint, params) => endpoint.api //
2454
+ .post('/user', params)
2455
+ .then((r) => r.data);
2456
+
2457
+ // TODO
2458
+ const billingPlaceholder = {};
2459
+
2460
+ const getNotifications = async (endpoint) => endpoint.api //
2461
+ .get('/notifications')
2462
+ .then((r) => r.data);
2585
2463
 
2586
2464
  /**
2587
- * Create an array containing a sequence of integers, e.g. [START, START+1, START+2, ...] This is frequently useful
2588
- * in rendering operations when there is no source array to .map() across.
2465
+ * Get the user's available profiles. The current profile will be marked with `current: true`.
2466
+ *
2467
+ * ```typescript
2468
+ * import {Profiles} from '@verdocs/js-sdk/Users';
2469
+ *
2470
+ * const profiles = await Profiles.getProfiles()
2471
+ * ```
2589
2472
  */
2590
- const integerSequence = (start, count) => Array(count)
2591
- .fill(1)
2592
- .map((_, index) => index + start);
2473
+ const getProfiles = (endpoint) => endpoint.api //
2474
+ .get('/profiles')
2475
+ .then((r) => r.data);
2593
2476
  /**
2594
- * Format a profile's full name
2477
+ * Get the user's available profiles. The current profile will be marked with `current: true`.
2478
+ *
2479
+ * ```typescript
2480
+ * import {Profiles} from '@verdocs/js-sdk/Users';
2481
+ *
2482
+ * const profiles = await Profiles.getCurrentProfile()
2483
+ * ```
2595
2484
  */
2596
- const formatFullName = (profile) => profile ? `${capitalize(profile.first_name)} ${capitalize(profile.last_name)}` : 'Invalid User';
2485
+ const getCurrentProfile = (endpoint) => endpoint.api //
2486
+ .get('/profiles')
2487
+ .then((r) => (r.data || []).find((profile) => profile.current));
2597
2488
  /**
2598
- * Format a profile's initials
2489
+ * Get a list of system roles.
2490
+ *
2491
+ * ```typescript
2492
+ * import {Profiles} from '@verdocs/js-sdk/Users';
2493
+ *
2494
+ * const roles = await Profiles.getRoles();
2495
+ * ```
2599
2496
  */
2600
- const formatInitials = (profile) => profile ? `${capitalize(profile.first_name).charAt(0)} ${capitalize(profile.last_name).charAt(0)}` : '--';
2497
+ const getRoles = (endpoint) => endpoint.api //
2498
+ .get('/roles')
2499
+ .then((r) => r.data);
2601
2500
  /**
2602
- * Generate suggested initials for a full name, e.g. "John Doe" will yield "JD".
2501
+ * Get a list of system roles.
2502
+ *
2503
+ * ```typescript
2504
+ * import {Profiles} from '@verdocs/js-sdk/Users';
2505
+ *
2506
+ * const permissions = await Profiles.getPermissions();
2507
+ * ```
2603
2508
  */
2604
- const fullNameToInitials = (name) => name
2605
- .split(' ')
2606
- .map((word) => word[0])
2607
- .join('');
2509
+ const getPermissions = (endpoint) => endpoint.api //
2510
+ .get('/permissions')
2511
+ .then((r) => r.data);
2512
+ /**
2513
+ * Create a profile. If the caller does not have a "current" profile set, the new profile will be made current.
2514
+ *
2515
+ * ```typescript
2516
+ * import {Profiles} from '@verdocs/js-sdk/Users';
2517
+ *
2518
+ * const newProfile = await Profiles.createProfile({ first_name: 'FIRST', last_name: 'LAST', email: 'EMAIL' });
2519
+ * ```
2520
+ */
2521
+ const createProfile = (endpoint, params) => endpoint.api //
2522
+ .post('/profiles', params)
2523
+ .then((r) => r.data);
2524
+ /**
2525
+ * Get a profile. The caller must have admin access to the given profile.
2526
+ * TODO: Add a "public" profile endpoint for public pages
2527
+ *
2528
+ * ```typescript
2529
+ * import {Profiles} from '@verdocs/js-sdk/Users';
2530
+ *
2531
+ * const profile = await Profiles.getProfile('PROFILEID');
2532
+ * ```
2533
+ */
2534
+ const getProfile = (endpoint, profileId) => endpoint.api //
2535
+ .get(`/profiles/${profileId}`)
2536
+ .then((r) => r.data);
2537
+ /**
2538
+ * Get a profile's permissions. The caller must have admin access to the given profile.
2539
+ *
2540
+ * ```typescript
2541
+ * import {Profiles} from '@verdocs/js-sdk/Users';
2542
+ *
2543
+ * const permissions = await Profiles.getProfilePermissions('PROFILEID');
2544
+ * ```
2545
+ */
2546
+ const getProfilePermissions = (endpoint, profileId) => endpoint.api //
2547
+ .get(`/profiles/${profileId}/permissions`)
2548
+ .then((r) => r.data);
2549
+ /**
2550
+ * Get a profile's groups.
2551
+ *
2552
+ * ```typescript
2553
+ * import {Profiles} from '@verdocs/js-sdk/Users';
2554
+ *
2555
+ * const groups = await Profiles.getProfileGroups('PROFILEID');
2556
+ * ```
2557
+ */
2558
+ const getProfileGroups = (endpoint, profileId) => endpoint.api //
2559
+ .get(`/profiles/${profileId}/groups`)
2560
+ .then((r) => r.data);
2561
+ /**
2562
+ * Switch the caller's "current" profile. The current profile is used for permissions checking and profile_id field settings
2563
+ * for most operations in Verdocs. It is important to select the appropropriate profile before calling other API functions.
2564
+ *
2565
+ * ```typescript
2566
+ * import {Profiles} from '@verdocs/js-sdk/Users';
2567
+ *
2568
+ * const newProfile = await Profiles.switchProfile('PROFILEID');
2569
+ * ```
2570
+ */
2571
+ const switchProfile = (endpoint, profileId) => endpoint.api //
2572
+ .post(`/profiles/${profileId}/switch`)
2573
+ .then((r) => r.data);
2574
+ /**
2575
+ * Update a profile. For future expansion, the profile ID to update is required, but currently this must also be the
2576
+ * "current" profile for the caller.
2577
+ *
2578
+ * ```typescript
2579
+ * import {Profiles} from '@verdocs/js-sdk/Users';
2580
+ *
2581
+ * const newProfile = await Profiles.updateProfile('PROFILEID');
2582
+ * ```
2583
+ */
2584
+ const updateProfile = (endpoint, profileId, params) => endpoint.api //
2585
+ .put(`/profiles/${profileId}`, params)
2586
+ .then((r) => r.data);
2587
+ /**
2588
+ * Delete a profile. If the requested profile is the caller's curent profile, the next available profile will be selected.
2589
+ *
2590
+ * ```typescript
2591
+ * import {Profiles} from '@verdocs/js-sdk/Users';
2592
+ *
2593
+ * await Profiles.deleteProfile('PROFILEID');
2594
+ * ```
2595
+ */
2596
+ const deleteProfile = (endpoint, profileId) => endpoint.api //
2597
+ .delete(`/profiles/${profileId}`)
2598
+ .then((r) => r.data);
2599
+ /**
2600
+ * Create a user account and parent organization. This endpoint is for creating a new organization. Users joining an
2601
+ * existing organization should be invited, and follow their invitation links/instructions to create their accounts.
2602
+ *
2603
+ * ```typescript
2604
+ * import {Profiles} from '@verdocs/js-sdk/Users';
2605
+ *
2606
+ * const newAccount = await Profiles.createBusinessAccount({
2607
+ * orgName: 'ORG', email: 'a@b.com', password: '12345678', firstName: 'FIRST', lastName: 'LAST'
2608
+ * });
2609
+ * ```
2610
+ */
2611
+ const createBusinessAccount = (endpoint, params) => endpoint.api //
2612
+ .post('/user/business', params)
2613
+ .then((r) => r.data);
2614
+ const recordSignupSurvey = (endpoint, params) => endpoint.api //
2615
+ .post('/user/signup', params)
2616
+ .then((r) => r.data);
2608
2617
 
2609
2618
  exports.AtoB = AtoB;
2610
2619
  exports.Countries = Countries;
@@ -2687,6 +2696,7 @@ exports.getEnvelopeDocumentPageDisplayUri = getEnvelopeDocumentPageDisplayUri;
2687
2696
  exports.getEnvelopeFile = getEnvelopeFile;
2688
2697
  exports.getEnvelopeRecipients = getEnvelopeRecipients;
2689
2698
  exports.getEnvelopeReminder = getEnvelopeReminder;
2699
+ exports.getEnvelopesByTemplateId = getEnvelopesByTemplateId;
2690
2700
  exports.getEnvelopesSummary = getEnvelopesSummary;
2691
2701
  exports.getFieldAttachment = getFieldAttachment;
2692
2702
  exports.getGroup = getGroup;