@graph8/sdk 0.373.0 → 0.374.0
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/README.md +3 -3
- package/blocks/_foundation/README.md +47 -0
- package/blocks/_foundation/block.json +13 -0
- package/blocks/_foundation/components/auth-gate.tsx +363 -0
- package/blocks/_foundation/server/auth.ts +815 -0
- package/blocks/campaigns/README.md +1 -1
- package/blocks/campaigns/block.json +34 -1
- package/blocks/campaigns/components/campaign-links.tsx +180 -0
- package/blocks/company-intelligence/components/research-run.tsx +6 -2
- package/blocks/crm/README.md +19 -0
- package/blocks/crm/block.json +205 -6
- package/blocks/crm/components/bulk-update.tsx +119 -0
- package/blocks/crm/components/contact-documents-tab.tsx +144 -0
- package/blocks/crm/components/contact-insights-tab.tsx +118 -0
- package/blocks/crm/components/contact-lookup-link.tsx +47 -6
- package/blocks/crm/components/contact-row-actions.tsx +43 -0
- package/blocks/crm/components/contact-tag-chips.tsx +113 -0
- package/blocks/crm/components/contacts-screen.tsx +46 -5
- package/blocks/crm/components/owner-assign.tsx +132 -0
- package/blocks/crm/components/record-open-links.tsx +79 -0
- package/blocks/crm/components/tag-assign.tsx +112 -0
- package/blocks/crm/components/tag-manager.tsx +178 -0
- package/blocks/crm/components/unlock-info-button.tsx +82 -0
- package/blocks/crm/components/verify-email-button.tsx +93 -0
- package/blocks/crm/hooks/use-contact-tags.ts +29 -0
- package/blocks/crm/lib/record-actions.ts +102 -0
- package/blocks/deals/README.md +1 -0
- package/blocks/deals/block.json +17 -0
- package/blocks/deals/components/create-deal-button.tsx +109 -0
- package/blocks/deals/components/deal-detail.tsx +5 -3
- package/blocks/dialer/components/call-log-screen.tsx +6 -1
- package/blocks/documents/README.md +86 -0
- package/blocks/documents/block.json +378 -0
- package/blocks/documents/components/account-bar.tsx +48 -0
- package/blocks/documents/components/correspondence-form.tsx +95 -0
- package/blocks/documents/components/correspondence-import.tsx +106 -0
- package/blocks/documents/components/correspondence-panel.tsx +191 -0
- package/blocks/documents/components/documents-screen.tsx +76 -0
- package/blocks/documents/components/library-document-detail.tsx +175 -0
- package/blocks/documents/components/library-document-form.tsx +105 -0
- package/blocks/documents/components/library-folder-form.tsx +74 -0
- package/blocks/documents/components/library-panel.tsx +235 -0
- package/blocks/documents/components/library-upload.tsx +160 -0
- package/blocks/documents/components/question-row.tsx +130 -0
- package/blocks/documents/components/questionnaire-detail.tsx +197 -0
- package/blocks/documents/components/questionnaires-panel.tsx +173 -0
- package/blocks/documents/components/record-documents-tab.tsx +109 -0
- package/blocks/documents/lib/documents-types.ts +252 -0
- package/blocks/documents/lib/download.ts +64 -0
- package/blocks/documents/recipes/follow-a-questionnaire.ts +26 -0
- package/blocks/documents/recipes/log-account-correspondence.ts +27 -0
- package/blocks/documents/recipes/organize-the-library.ts +25 -0
- package/blocks/email-signatures/README.md +123 -0
- package/blocks/email-signatures/block.json +341 -0
- package/blocks/email-signatures/components/email-signatures-screen.tsx +166 -0
- package/blocks/email-signatures/components/signature-assign-template.tsx +119 -0
- package/blocks/email-signatures/components/signature-block-editor.tsx +87 -0
- package/blocks/email-signatures/components/signature-campaign-form.tsx +165 -0
- package/blocks/email-signatures/components/signature-campaigns.tsx +75 -0
- package/blocks/email-signatures/components/signature-identity-form.tsx +131 -0
- package/blocks/email-signatures/components/signature-mailboxes.tsx +195 -0
- package/blocks/email-signatures/components/signature-policy.tsx +130 -0
- package/blocks/email-signatures/components/signature-preview.tsx +119 -0
- package/blocks/email-signatures/components/signature-report.tsx +77 -0
- package/blocks/email-signatures/components/signature-template-details.tsx +112 -0
- package/blocks/email-signatures/components/signature-template-form.tsx +109 -0
- package/blocks/email-signatures/hooks/use-signatures-data.ts +93 -0
- package/blocks/email-signatures/lib/signatures-types.ts +392 -0
- package/blocks/email-signatures/recipes/assign-a-signature-to-a-new-hire.ts +32 -0
- package/blocks/email-signatures/recipes/create-a-template-and-make-it-default.ts +35 -0
- package/blocks/email-signatures/recipes/signature-engagement-report.ts +16 -0
- package/blocks/enrichment/README.md +22 -4
- package/blocks/enrichment/block.json +145 -5
- package/blocks/enrichment/components/auto-enrichment-configs.tsx +134 -0
- package/blocks/enrichment/components/auto-enrichment-settings.tsx +82 -0
- package/blocks/enrichment/components/bulk-enrich-panel.tsx +7 -7
- package/blocks/enrichment/components/enrichment-activity.tsx +93 -0
- package/blocks/enrichment/components/enrichment-job-detail.tsx +18 -0
- package/blocks/enrichment/components/enrichment-screen.tsx +34 -3
- package/blocks/enrichment/components/list-pipelines.tsx +97 -0
- package/blocks/enrichment/components/pipeline-detail.tsx +214 -0
- package/blocks/enrichment/components/pipeline-editor.tsx +144 -0
- package/blocks/enrichment/components/pipeline-templates.tsx +90 -0
- package/blocks/enrichment/lib/enrichment-types.ts +12 -0
- package/blocks/enrichment/lib/pipeline-types.ts +234 -0
- package/blocks/forms/README.md +2 -0
- package/blocks/forms/components/forms-screen.tsx +6 -3
- package/blocks/inbox/README.md +40 -4
- package/blocks/inbox/block.json +130 -8
- package/blocks/inbox/components/blocked-senders.tsx +103 -0
- package/blocks/inbox/components/compose-message.tsx +193 -0
- package/blocks/inbox/components/inbox-screen.tsx +54 -6
- package/blocks/inbox/components/inbox-search.tsx +106 -0
- package/blocks/inbox/components/tag-manager.tsx +136 -0
- package/blocks/inbox/components/thread-actions.tsx +133 -0
- package/blocks/inbox/components/thread-view.tsx +2 -0
- package/blocks/inbox/hooks/use-inbox-mailboxes.ts +31 -0
- package/blocks/inbox/lib/inbox-manage.ts +227 -0
- package/blocks/newsletter/components/newsletter-screen.tsx +3 -1
- package/blocks/pages/block.json +10 -1
- package/blocks/pages/hooks/use-landing-page-paths.ts +60 -0
- package/blocks/programmatic-pages/README.md +82 -0
- package/blocks/programmatic-pages/block.json +292 -0
- package/blocks/programmatic-pages/components/programmatic-campaign-form.tsx +136 -0
- package/blocks/programmatic-pages/components/programmatic-generate.tsx +112 -0
- package/blocks/programmatic-pages/components/programmatic-generated-pages.tsx +210 -0
- package/blocks/programmatic-pages/components/programmatic-overview.tsx +84 -0
- package/blocks/programmatic-pages/components/programmatic-pages-screen.tsx +221 -0
- package/blocks/programmatic-pages/components/programmatic-preview.tsx +66 -0
- package/blocks/programmatic-pages/components/programmatic-publish.tsx +102 -0
- package/blocks/programmatic-pages/components/programmatic-settings.tsx +96 -0
- package/blocks/programmatic-pages/components/programmatic-source-fields.tsx +75 -0
- package/blocks/programmatic-pages/components/programmatic-source.tsx +106 -0
- package/blocks/programmatic-pages/components/programmatic-variables.tsx +84 -0
- package/blocks/programmatic-pages/hooks/use-programmatic-workspace.ts +19 -0
- package/blocks/programmatic-pages/lib/programmatic-types.ts +330 -0
- package/blocks/programmatic-pages/recipes/create-a-programmatic-campaign.ts +23 -0
- package/blocks/programmatic-pages/recipes/render-and-follow.ts +21 -0
- package/blocks/programmatic-pages/recipes/review-then-publish.ts +17 -0
- package/blocks/quotes/README.md +25 -1
- package/blocks/quotes/block.json +123 -6
- package/blocks/quotes/components/manual-products.tsx +144 -0
- package/blocks/quotes/components/quote-batch-actions.tsx +86 -0
- package/blocks/quotes/components/quote-documents.tsx +94 -0
- package/blocks/quotes/components/quote-evidence.tsx +95 -0
- package/blocks/quotes/components/quote-list.tsx +23 -5
- package/blocks/quotes/components/quote-settings.tsx +184 -0
- package/blocks/quotes/components/quote-templates.tsx +189 -0
- package/blocks/quotes/components/quote-viewer.tsx +16 -3
- package/blocks/quotes/components/quotes-screen.tsx +15 -5
- package/blocks/quotes/lib/quote-admin.ts +264 -0
- package/blocks/sequencer/README.md +55 -0
- package/blocks/sequencer/block.json +172 -5
- package/blocks/sequencer/components/enrollment-withdraw.tsx +71 -0
- package/blocks/sequencer/components/sequence-builder.tsx +13 -3
- package/blocks/sequencer/components/sequence-campaign-link.tsx +78 -0
- package/blocks/sequencer/components/sequence-detail.tsx +6 -0
- package/blocks/sequencer/components/sequence-edit-form.tsx +111 -0
- package/blocks/sequencer/components/sequence-enrollments.tsx +2 -1
- package/blocks/sequencer/components/sequence-manage-actions.tsx +119 -0
- package/blocks/sequencer/components/sequence-senders-panel.tsx +84 -0
- package/blocks/sequencer/components/sequence-templates.tsx +160 -0
- package/blocks/sequencer/components/sequences-screen.tsx +36 -6
- package/blocks/sequencer/components/step-ai-assist.tsx +196 -0
- package/blocks/sequencer/components/step-editor.tsx +10 -1
- package/blocks/sequencer/components/step-templates.tsx +224 -0
- package/blocks/sequencer/lib/sequencer-manage.ts +235 -0
- package/blocks/sequencer/lib/sequencer-utils.ts +4 -0
- package/blocks/signals/README.md +39 -0
- package/blocks/signals/block.json +287 -0
- package/blocks/signals/components/auto-seed-panel.tsx +111 -0
- package/blocks/signals/components/listener-form.tsx +184 -0
- package/blocks/signals/components/matched-posts.tsx +195 -0
- package/blocks/signals/components/post-engagements.tsx +114 -0
- package/blocks/signals/components/record-social-tab.tsx +49 -0
- package/blocks/signals/components/social-listeners-screen.tsx +250 -0
- package/blocks/signals/components/social-settings.tsx +128 -0
- package/blocks/signals/components/source-lists.tsx +83 -0
- package/blocks/signals/components/top-engagers.tsx +68 -0
- package/blocks/signals/components/x-integration.tsx +129 -0
- package/blocks/signals/lib/signals-types.ts +228 -0
- package/blocks/signals/recipes/preview-and-create-a-listener.ts +16 -0
- package/blocks/signals/recipes/spend-against-caps.ts +14 -0
- package/blocks/signals/recipes/triage-matched-posts.ts +18 -0
- package/blocks/visitors/components/traffic-chart.tsx +3 -2
- package/blocks/web-analytics/README.md +102 -0
- package/blocks/web-analytics/block.json +292 -0
- package/blocks/web-analytics/components/acquisition-tab.tsx +154 -0
- package/blocks/web-analytics/components/analytics-filters.tsx +161 -0
- package/blocks/web-analytics/components/audience-tab.tsx +135 -0
- package/blocks/web-analytics/components/conversions-tab.tsx +128 -0
- package/blocks/web-analytics/components/engagement-tab.tsx +123 -0
- package/blocks/web-analytics/components/overview-tab.tsx +102 -0
- package/blocks/web-analytics/components/pages-tab.tsx +148 -0
- package/blocks/web-analytics/components/ranked-table.tsx +103 -0
- package/blocks/web-analytics/components/realtime-tab.tsx +103 -0
- package/blocks/web-analytics/components/report-card.tsx +71 -0
- package/blocks/web-analytics/components/web-analytics-screen.tsx +90 -0
- package/blocks/web-analytics/hooks/use-report.ts +48 -0
- package/blocks/web-analytics/lib/analytics-scope.ts +155 -0
- package/blocks/web-analytics/lib/page-path.ts +14 -0
- package/blocks/web-analytics/lib/web-analytics-types.ts +187 -0
- package/blocks/web-analytics/recipes/best-landing-pages.ts +26 -0
- package/blocks/web-analytics/recipes/form-conversion-check.ts +19 -0
- package/blocks/web-analytics/recipes/weekly-traffic-report.ts +37 -0
- package/blocks/workbench/README.md +54 -2
- package/blocks/workbench/block.json +234 -5
- package/blocks/workbench/components/workbench-auto-enrich.tsx +88 -0
- package/blocks/workbench/components/workbench-automation.tsx +36 -0
- package/blocks/workbench/components/workbench-connections.tsx +136 -0
- package/blocks/workbench/components/workbench-crm-import.tsx +91 -0
- package/blocks/workbench/components/workbench-datafile-import.tsx +154 -0
- package/blocks/workbench/components/workbench-grid.tsx +70 -7
- package/blocks/workbench/components/workbench-pipelines.tsx +173 -0
- package/blocks/workbench/components/workbench-row-actions.tsx +8 -1
- package/blocks/workbench/components/workbench-schedule.tsx +191 -0
- package/blocks/workbench/components/workbench-screen.tsx +50 -17
- package/blocks/workbench/components/workbench-send-to-sequence.tsx +116 -0
- package/blocks/workbench/components/workbench-table-settings.tsx +53 -0
- package/blocks/workbench/components/workbench-triggers.tsx +178 -0
- package/blocks/workbench/hooks/use-connected-crms.ts +29 -0
- package/blocks/workbench/lib/api-status.ts +8 -0
- package/blocks/workbench/lib/automation-types.ts +257 -0
- package/dist/cli.js +257 -51
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +1 -1
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/react.browser.js +1 -1
- package/dist/react.browser.js.map +1 -1
- package/dist/react.browser.mjs +1 -1
- package/dist/react.browser.mjs.map +1 -1
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +1 -1
- package/dist/react.mjs.map +1 -1
- package/dist/types/blocks/auth.d.ts +45 -0
- package/dist/types/blocks/create.d.ts +5 -2
- package/dist/types/version.d.ts +2 -2
- package/docs/api/forms.md +1 -1
- package/package.json +1 -1
- package/stacks/6sense/pages/contacts.tsx.tmpl +2 -0
- package/stacks/6sense.json +20 -0
- package/stacks/apollo/pages/calls.tsx.tmpl +9 -9
- package/stacks/apollo/pages/campaigns.tsx.tmpl +27 -0
- package/stacks/apollo/pages/contacts.tsx.tmpl +6 -0
- package/stacks/apollo/pages/inbox-contact.tsx.tmpl +3 -3
- package/stacks/apollo/pages/lists.tsx.tmpl +14 -2
- package/stacks/apollo/pages/sequences.tsx.tmpl +8 -2
- package/stacks/apollo.json +33 -2
- package/stacks/clay/pages/contacts.tsx.tmpl +2 -0
- package/stacks/clay/pages/lists.tsx.tmpl +14 -2
- package/stacks/gong/pages/calls.tsx.tmpl +9 -9
- package/stacks/gong/pages/contacts.tsx.tmpl +2 -0
- package/stacks/hootsuite/pages/contacts.tsx.tmpl +2 -0
- package/stacks/hootsuite.json +10 -0
- package/stacks/hubspot/pages/campaigns.tsx.tmpl +30 -0
- package/stacks/hubspot/pages/companies.tsx.tmpl +2 -0
- package/stacks/hubspot/pages/contacts.tsx.tmpl +6 -0
- package/stacks/hubspot/pages/deals.tsx.tmpl +4 -0
- package/stacks/hubspot/pages/forms.tsx.tmpl +36 -0
- package/stacks/hubspot/pages/inbox.tsx.tmpl +2 -2
- package/stacks/hubspot/pages/sequences.tsx.tmpl +8 -2
- package/stacks/hubspot/pages/web-analytics.tsx.tmpl +14 -0
- package/stacks/hubspot.json +45 -2
- package/stacks/intercom/pages/inbox-contact.tsx.tmpl +3 -3
- package/stacks/intercom.json +10 -0
- package/stacks/mailchimp/pages/contacts.tsx.tmpl +2 -0
- package/stacks/mailchimp/pages/forms.tsx.tmpl +36 -0
- package/stacks/mailchimp.json +2 -1
- package/stacks/outreach/pages/calls.tsx.tmpl +9 -9
- package/stacks/outreach/pages/campaigns.tsx.tmpl +27 -0
- package/stacks/outreach/pages/contacts.tsx.tmpl +2 -0
- package/stacks/outreach/pages/inbox-contact.tsx.tmpl +3 -3
- package/stacks/outreach/pages/sequences.tsx.tmpl +8 -2
- package/stacks/outreach.json +13 -2
- package/stacks/pipedrive/pages/contacts.tsx.tmpl +2 -0
- package/stacks/pipedrive/pages/inbox.tsx.tmpl +2 -2
- package/stacks/pipedrive.json +10 -0
- package/stacks/salesforce/pages/companies.tsx.tmpl +2 -0
- package/stacks/salesforce/pages/contacts.tsx.tmpl +6 -0
- package/stacks/salesforce/pages/deals.tsx.tmpl +4 -0
- package/stacks/salesforce.json +10 -0
- package/stacks/zoominfo/pages/contacts.tsx.tmpl +4 -0
- package/stacks/zoominfo/pages/lists.tsx.tmpl +14 -2
- package/stacks/zoominfo.json +20 -0
- package/styles/styles.css +1 -1
- package/templates/auth/auth-route.ts.tmpl +16 -0
- package/templates/auth/g8.auth.anonymous.ts.tmpl +15 -0
- package/templates/auth/g8.auth.basic.ts.tmpl +17 -0
- package/templates/auth/g8.auth.jwt.ts.tmpl +23 -0
- package/templates/auth/g8.auth.password.ts.tmpl +18 -0
- package/templates/auth/g8.auth.session.ts.tmpl +23 -0
- package/templates/auth/middleware.ts.tmpl +14 -0
- package/templates/next-app/README.md.tmpl +5 -0
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
5
|
import { useRouter } from "next/navigation";
|
|
6
6
|
import { DealsScreen } from "@/components/g8/deals/deals-screen";
|
|
7
|
+
import { RecordDocumentsTab } from "@/components/g8/documents/record-documents-tab";
|
|
7
8
|
import { CreateQuoteFromDeal } from "@/components/g8/quotes/create-quote-from-deal";
|
|
8
9
|
|
|
9
10
|
/** A deal opens its quotes, its contacts and (from a contact) their company. */
|
|
@@ -15,6 +16,9 @@ export default function Page() {
|
|
|
15
16
|
renderQuoteActions={(deal) => <CreateQuoteFromDeal dealId={deal.id} />}
|
|
16
17
|
onOpenContact={(id) => router.push(`/contacts?id=${id}`)}
|
|
17
18
|
onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
|
|
19
|
+
extraDetailTabs={[
|
|
20
|
+
{ id: "documents", label: "Documents", render: (d) => <RecordDocumentsTab contactId={d.primary_contact?.id} scope="account" onOpenDocuments={(href) => router.push(href)} /> },
|
|
21
|
+
]}
|
|
18
22
|
/>
|
|
19
23
|
);
|
|
20
24
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
|
+
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
6
|
+
import { ContactLookupLink, splitName } from "@/components/g8/crm/contact-lookup-link";
|
|
7
|
+
import { FormsScreen } from "@/components/g8/forms/forms-screen";
|
|
8
|
+
|
|
9
|
+
/** The first email-looking value a person typed into the form. Identifying fields are redacted for some keys: then there is no email and no button. */
|
|
10
|
+
function emailOf(data: Record<string, unknown> | undefined): string | null {
|
|
11
|
+
for (const [key, value] of Object.entries(data ?? {})) {
|
|
12
|
+
if (/e-?mail/i.test(key) && typeof value === "string" && value.includes("@") && !value.includes("*")) return value.trim();
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** The submitter's name when the form asked for one. */
|
|
18
|
+
function nameOf(data: Record<string, unknown> | undefined): { firstName?: string; lastName?: string } {
|
|
19
|
+
const get = (re: RegExp) => Object.entries(data ?? {}).find(([k, v]) => re.test(k) && typeof v === "string" && v.trim())?.[1] as string | undefined;
|
|
20
|
+
const first = get(/^first[ _-]?name$/i);
|
|
21
|
+
const last = get(/^last[ _-]?name$/i);
|
|
22
|
+
if (first || last) return { firstName: first?.trim(), lastName: last?.trim() };
|
|
23
|
+
return splitName(get(/^(full[ _-]?)?name$/i));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A submission opens the contact it created, or adds the person when no contact has that email. */
|
|
27
|
+
export default function Page() {
|
|
28
|
+
const router = useRouter();
|
|
29
|
+
return (
|
|
30
|
+
<FormsScreen
|
|
31
|
+
renderSubmissionActions={(submission) => (
|
|
32
|
+
<ContactLookupLink email={emailOf(submission.form_data)} createIfMissing={nameOf(submission.form_data)} onOpen={(id) => router.push(`/contacts?id=${id}`)} />
|
|
33
|
+
)}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -10,7 +10,7 @@ import { ApiError } from "@/components/g8/_foundation/api-error";
|
|
|
10
10
|
import { EmptyState } from "@/components/g8/_foundation/empty-state";
|
|
11
11
|
import { useG8Query } from "@/components/g8/_foundation/hooks/use-g8-query";
|
|
12
12
|
import { g8Get } from "@/components/g8/_foundation/lib/g8-client";
|
|
13
|
-
import { ContactLookupLink } from "@/components/g8/crm/contact-lookup-link";
|
|
13
|
+
import { ContactLookupLink, splitName } from "@/components/g8/crm/contact-lookup-link";
|
|
14
14
|
import { DealDetail } from "@/components/g8/deals/deal-detail";
|
|
15
15
|
import { useDefaultOwner } from "@/components/g8/deals/hooks/use-default-owner";
|
|
16
16
|
import { InboxScreen } from "@/components/g8/inbox/inbox-screen";
|
|
@@ -94,7 +94,7 @@ export default function Page() {
|
|
|
94
94
|
<InboxScreen
|
|
95
95
|
renderDetailActions={(thread) => (
|
|
96
96
|
<>
|
|
97
|
-
<ContactLookupLink email={thread.contact?.email} onOpen={(id) => router.push(`/contacts?id=${id}`)} />
|
|
97
|
+
<ContactLookupLink email={thread.contact?.email} createIfMissing={splitName(thread.contact?.name)} onOpen={(id) => router.push(`/contacts?id=${id}`)} />
|
|
98
98
|
<CreateDeal thread={thread} />
|
|
99
99
|
</>
|
|
100
100
|
)}
|
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
4
|
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
5
|
import { useRouter } from "next/navigation";
|
|
6
|
+
import { SequenceCampaignLink } from "@/components/g8/sequencer/sequence-campaign-link";
|
|
6
7
|
import { SequencesScreen } from "@/components/g8/sequencer/sequences-screen";
|
|
7
8
|
|
|
8
|
-
/** An enrolled person opens in Contacts. */
|
|
9
|
+
/** An enrolled person opens in Contacts; a sequence opens, links or unlinks its campaign. */
|
|
9
10
|
export default function Page() {
|
|
10
11
|
const router = useRouter();
|
|
11
|
-
return
|
|
12
|
+
return (
|
|
13
|
+
<SequencesScreen
|
|
14
|
+
onOpenContact={(id) => router.push(`/contacts?id=${id}`)}
|
|
15
|
+
renderDetailActions={(sequence) => <SequenceCampaignLink sequence={sequence} onOpenCampaign={(id) => router.push(`/campaigns?id=${encodeURIComponent(id)}`)} />}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
12
18
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
|
+
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
6
|
+
import { WebAnalyticsScreen } from "@/components/g8/web-analytics/web-analytics-screen";
|
|
7
|
+
import { useLandingPagePaths } from "@/components/g8/pages/hooks/use-landing-page-paths";
|
|
8
|
+
|
|
9
|
+
/** A page in the reports opens its landing page, matched by the published address only (never by name). */
|
|
10
|
+
export default function Page() {
|
|
11
|
+
const router = useRouter();
|
|
12
|
+
const landing = useLandingPagePaths();
|
|
13
|
+
return <WebAnalyticsScreen resolveLandingPage={landing.resolve} onOpenLandingPage={(id) => router.push(`/pages?id=${encodeURIComponent(id)}`)} />;
|
|
14
|
+
}
|
package/stacks/hubspot.json
CHANGED
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
"objects",
|
|
12
12
|
"lists",
|
|
13
13
|
"visitors",
|
|
14
|
+
"web-analytics",
|
|
14
15
|
"forms",
|
|
15
16
|
"mailboxes",
|
|
17
|
+
"email-signatures",
|
|
16
18
|
"sequencer",
|
|
17
19
|
"campaigns",
|
|
18
20
|
"inbox",
|
|
@@ -22,10 +24,12 @@
|
|
|
22
24
|
"webchat",
|
|
23
25
|
"appointments",
|
|
24
26
|
"pages",
|
|
27
|
+
"programmatic-pages",
|
|
25
28
|
"social-ads",
|
|
26
29
|
"lifecycle",
|
|
27
30
|
"deals",
|
|
28
31
|
"quotes",
|
|
32
|
+
"documents",
|
|
29
33
|
"analytics",
|
|
30
34
|
"workflows"
|
|
31
35
|
],
|
|
@@ -79,6 +83,16 @@
|
|
|
79
83
|
"group": "Signals",
|
|
80
84
|
"description": "Who is on your website right now"
|
|
81
85
|
},
|
|
86
|
+
{
|
|
87
|
+
"label": "Web analytics",
|
|
88
|
+
"path": "/web-analytics",
|
|
89
|
+
"block": "web-analytics",
|
|
90
|
+
"component": "WebAnalyticsScreen",
|
|
91
|
+
"icon": "ChartNoAxesCombined",
|
|
92
|
+
"group": "Signals",
|
|
93
|
+
"description": "Traffic, audience, engagement and conversions for your sites",
|
|
94
|
+
"page": "pages/web-analytics.tsx.tmpl"
|
|
95
|
+
},
|
|
82
96
|
{
|
|
83
97
|
"label": "Inbox",
|
|
84
98
|
"path": "/inbox",
|
|
@@ -134,7 +148,8 @@
|
|
|
134
148
|
"component": "CampaignsScreen",
|
|
135
149
|
"icon": "Rocket",
|
|
136
150
|
"group": "Engage",
|
|
137
|
-
"description": "Campaigns from brief to launch"
|
|
151
|
+
"description": "Campaigns from brief to launch",
|
|
152
|
+
"page": "pages/campaigns.tsx.tmpl"
|
|
138
153
|
},
|
|
139
154
|
{
|
|
140
155
|
"label": "Newsletter",
|
|
@@ -170,7 +185,8 @@
|
|
|
170
185
|
"component": "FormsScreen",
|
|
171
186
|
"icon": "ClipboardList",
|
|
172
187
|
"group": "Marketing",
|
|
173
|
-
"description": "Forms and their submissions"
|
|
188
|
+
"description": "Forms and their submissions",
|
|
189
|
+
"page": "pages/forms.tsx.tmpl"
|
|
174
190
|
},
|
|
175
191
|
{
|
|
176
192
|
"label": "Pages",
|
|
@@ -181,6 +197,15 @@
|
|
|
181
197
|
"group": "Marketing",
|
|
182
198
|
"description": "Landing pages, brand kit and content"
|
|
183
199
|
},
|
|
200
|
+
{
|
|
201
|
+
"label": "Programmatic pages",
|
|
202
|
+
"path": "/programmatic-pages",
|
|
203
|
+
"block": "programmatic-pages",
|
|
204
|
+
"component": "ProgrammaticPagesScreen",
|
|
205
|
+
"icon": "Layers",
|
|
206
|
+
"group": "Marketing",
|
|
207
|
+
"description": "A page per row: render, preview, publish and roll back"
|
|
208
|
+
},
|
|
184
209
|
{
|
|
185
210
|
"label": "Social",
|
|
186
211
|
"path": "/social",
|
|
@@ -239,6 +264,15 @@
|
|
|
239
264
|
"description": "Follow-ups due across your records",
|
|
240
265
|
"page": "pages/tasks.tsx.tmpl"
|
|
241
266
|
},
|
|
267
|
+
{
|
|
268
|
+
"label": "Documents",
|
|
269
|
+
"path": "/documents",
|
|
270
|
+
"block": "documents",
|
|
271
|
+
"component": "DocumentsScreen",
|
|
272
|
+
"icon": "FileText",
|
|
273
|
+
"group": "Work",
|
|
274
|
+
"description": "Library, security questionnaires and account correspondence"
|
|
275
|
+
},
|
|
242
276
|
{
|
|
243
277
|
"label": "Workflows",
|
|
244
278
|
"path": "/workflows",
|
|
@@ -266,6 +300,15 @@
|
|
|
266
300
|
"group": "Setup",
|
|
267
301
|
"description": "Sending mailboxes and warmup health"
|
|
268
302
|
},
|
|
303
|
+
{
|
|
304
|
+
"label": "Email signatures",
|
|
305
|
+
"path": "/email-signatures",
|
|
306
|
+
"block": "email-signatures",
|
|
307
|
+
"component": "EmailSignaturesScreen",
|
|
308
|
+
"icon": "PenLine",
|
|
309
|
+
"group": "Setup",
|
|
310
|
+
"description": "One signature for the team, kept on every mailbox"
|
|
311
|
+
},
|
|
269
312
|
{
|
|
270
313
|
"label": "Suppressions",
|
|
271
314
|
"path": "/suppressions",
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
4
|
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
5
|
import { useRouter } from "next/navigation";
|
|
6
|
-
import { ContactLookupLink } from "@/components/g8/crm/contact-lookup-link";
|
|
6
|
+
import { ContactLookupLink, splitName } from "@/components/g8/crm/contact-lookup-link";
|
|
7
7
|
import { InboxScreen } from "@/components/g8/inbox/inbox-screen";
|
|
8
8
|
|
|
9
|
-
/** A conversation opens the sender when they are a contact in your CRM. */
|
|
9
|
+
/** A conversation opens the sender when they are a contact in your CRM, or adds them when they are new. */
|
|
10
10
|
export default function Page() {
|
|
11
11
|
const router = useRouter();
|
|
12
|
-
return <InboxScreen renderDetailActions={(thread) => <ContactLookupLink email={thread.contact?.email} onOpen={(id) => router.push(`/contacts?id=${id}`)} />} />;
|
|
12
|
+
return <InboxScreen renderDetailActions={(thread) => <ContactLookupLink email={thread.contact?.email} createIfMissing={splitName(thread.contact?.name)} onOpen={(id) => router.push(`/contacts?id=${id}`)} />} />;
|
|
13
13
|
}
|
package/stacks/intercom.json
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"crm",
|
|
10
10
|
"webchat",
|
|
11
11
|
"mailboxes",
|
|
12
|
+
"email-signatures",
|
|
12
13
|
"inbox",
|
|
13
14
|
"agents"
|
|
14
15
|
],
|
|
@@ -80,6 +81,15 @@
|
|
|
80
81
|
"icon": "Mail",
|
|
81
82
|
"group": "Setup",
|
|
82
83
|
"description": "Sending mailboxes and warmup health"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"label": "Email signatures",
|
|
87
|
+
"path": "/email-signatures",
|
|
88
|
+
"block": "email-signatures",
|
|
89
|
+
"component": "EmailSignaturesScreen",
|
|
90
|
+
"icon": "PenLine",
|
|
91
|
+
"group": "Setup",
|
|
92
|
+
"description": "One signature for the team, kept on every mailbox"
|
|
83
93
|
}
|
|
84
94
|
]
|
|
85
95
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
4
|
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
|
+
import { MemberPicker } from "@/components/g8/pickers/member-picker";
|
|
5
6
|
import { ContactsScreen } from "@/components/g8/crm/contacts-screen";
|
|
6
7
|
import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
|
|
7
8
|
import { AddToListButton } from "@/components/g8/lists/add-to-list-button";
|
|
@@ -12,6 +13,7 @@ export default function Page() {
|
|
|
12
13
|
const router = useRouter();
|
|
13
14
|
return (
|
|
14
15
|
<ContactsScreen
|
|
16
|
+
renderOwnerPicker={(picker) => <MemberPicker label="New owner" {...picker} />}
|
|
15
17
|
onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
|
|
16
18
|
renderBulkActions={({ ids, clear }) => <AddToListButton contactIds={ids} onAdded={clear} />}
|
|
17
19
|
renderDetailActions={(c) => (
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
|
+
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
6
|
+
import { ContactLookupLink, splitName } from "@/components/g8/crm/contact-lookup-link";
|
|
7
|
+
import { FormsScreen } from "@/components/g8/forms/forms-screen";
|
|
8
|
+
|
|
9
|
+
/** The first email-looking value a person typed into the form. Identifying fields are redacted for some keys: then there is no email and no button. */
|
|
10
|
+
function emailOf(data: Record<string, unknown> | undefined): string | null {
|
|
11
|
+
for (const [key, value] of Object.entries(data ?? {})) {
|
|
12
|
+
if (/e-?mail/i.test(key) && typeof value === "string" && value.includes("@") && !value.includes("*")) return value.trim();
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** The submitter's name when the form asked for one. */
|
|
18
|
+
function nameOf(data: Record<string, unknown> | undefined): { firstName?: string; lastName?: string } {
|
|
19
|
+
const get = (re: RegExp) => Object.entries(data ?? {}).find(([k, v]) => re.test(k) && typeof v === "string" && v.trim())?.[1] as string | undefined;
|
|
20
|
+
const first = get(/^first[ _-]?name$/i);
|
|
21
|
+
const last = get(/^last[ _-]?name$/i);
|
|
22
|
+
if (first || last) return { firstName: first?.trim(), lastName: last?.trim() };
|
|
23
|
+
return splitName(get(/^(full[ _-]?)?name$/i));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A submission opens the contact it created, or adds the person when no contact has that email. */
|
|
27
|
+
export default function Page() {
|
|
28
|
+
const router = useRouter();
|
|
29
|
+
return (
|
|
30
|
+
<FormsScreen
|
|
31
|
+
renderSubmissionActions={(submission) => (
|
|
32
|
+
<ContactLookupLink email={emailOf(submission.form_data)} createIfMissing={nameOf(submission.form_data)} onOpen={(id) => router.push(`/contacts?id=${id}`)} />
|
|
33
|
+
)}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
package/stacks/mailchimp.json
CHANGED
|
@@ -3,21 +3,21 @@
|
|
|
3
3
|
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
4
|
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
5
|
import { useRouter } from "next/navigation";
|
|
6
|
-
import {
|
|
7
|
-
import { UserRound } from "lucide-react";
|
|
6
|
+
import { RecordOpenLinks } from "@/components/g8/crm/record-open-links";
|
|
8
7
|
import { CallLogScreen } from "@/components/g8/dialer/call-log-screen";
|
|
9
8
|
|
|
10
|
-
/** A call opens the person it was made to. */
|
|
9
|
+
/** A call opens the person it was made to, and their company. The ids are checked against your CRM first, so a call whose contact is gone shows no link. */
|
|
11
10
|
export default function Page() {
|
|
12
11
|
const router = useRouter();
|
|
13
12
|
return (
|
|
14
13
|
<CallLogScreen
|
|
15
|
-
renderDetailActions={(call) =>
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
renderDetailActions={(call) => (
|
|
15
|
+
<RecordOpenLinks
|
|
16
|
+
contactId={call.contact_id}
|
|
17
|
+
onOpenContact={(id) => router.push(`/contacts?id=${id}`)}
|
|
18
|
+
onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
|
|
19
|
+
/>
|
|
20
|
+
)}
|
|
21
21
|
/>
|
|
22
22
|
);
|
|
23
23
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
|
+
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
6
|
+
import { CampaignLinksTab } from "@/components/g8/campaigns/campaign-links";
|
|
7
|
+
import { CampaignsScreen } from "@/components/g8/campaigns/campaigns-screen";
|
|
8
|
+
|
|
9
|
+
/** A campaign lists its landing pages, workflows and newsletters. This app has no screens for them, so the rows show names only. */
|
|
10
|
+
export default function Page() {
|
|
11
|
+
const router = useRouter();
|
|
12
|
+
return (
|
|
13
|
+
<CampaignsScreen
|
|
14
|
+
extraDetailTabs={[
|
|
15
|
+
{
|
|
16
|
+
id: "links",
|
|
17
|
+
label: "Links",
|
|
18
|
+
render: (c) => (
|
|
19
|
+
<CampaignLinksTab
|
|
20
|
+
campaignId={c.id}
|
|
21
|
+
/>
|
|
22
|
+
),
|
|
23
|
+
},
|
|
24
|
+
]}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
4
|
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
5
|
import { RecordMeetingsTab } from "@/components/g8/appointments/record-meetings-tab";
|
|
6
|
+
import { MemberPicker } from "@/components/g8/pickers/member-picker";
|
|
6
7
|
import { ContactsScreen } from "@/components/g8/crm/contacts-screen";
|
|
7
8
|
import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
|
|
8
9
|
import { CallContactButton } from "@/components/g8/dialer/call-contact-button";
|
|
@@ -18,6 +19,7 @@ export default function Page() {
|
|
|
18
19
|
const router = useRouter();
|
|
19
20
|
return (
|
|
20
21
|
<ContactsScreen
|
|
22
|
+
renderOwnerPicker={(picker) => <MemberPicker label="New owner" {...picker} />}
|
|
21
23
|
onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
|
|
22
24
|
renderBulkActions={({ ids, clear }) => (
|
|
23
25
|
<>
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
4
|
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
5
|
import { useRouter } from "next/navigation";
|
|
6
|
-
import { ContactLookupLink } from "@/components/g8/crm/contact-lookup-link";
|
|
6
|
+
import { ContactLookupLink, splitName } from "@/components/g8/crm/contact-lookup-link";
|
|
7
7
|
import { InboxScreen } from "@/components/g8/inbox/inbox-screen";
|
|
8
8
|
|
|
9
|
-
/** A conversation opens the sender when they are a contact in your CRM. */
|
|
9
|
+
/** A conversation opens the sender when they are a contact in your CRM, or adds them when they are new. */
|
|
10
10
|
export default function Page() {
|
|
11
11
|
const router = useRouter();
|
|
12
|
-
return <InboxScreen renderDetailActions={(thread) => <ContactLookupLink email={thread.contact?.email} onOpen={(id) => router.push(`/contacts?id=${id}`)} />} />;
|
|
12
|
+
return <InboxScreen renderDetailActions={(thread) => <ContactLookupLink email={thread.contact?.email} createIfMissing={splitName(thread.contact?.name)} onOpen={(id) => router.push(`/contacts?id=${id}`)} />} />;
|
|
13
13
|
}
|
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
4
|
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
5
|
import { useRouter } from "next/navigation";
|
|
6
|
+
import { SequenceCampaignLink } from "@/components/g8/sequencer/sequence-campaign-link";
|
|
6
7
|
import { SequencesScreen } from "@/components/g8/sequencer/sequences-screen";
|
|
7
8
|
|
|
8
|
-
/** An enrolled person opens in Contacts. */
|
|
9
|
+
/** An enrolled person opens in Contacts; a sequence opens, links or unlinks its campaign. */
|
|
9
10
|
export default function Page() {
|
|
10
11
|
const router = useRouter();
|
|
11
|
-
return
|
|
12
|
+
return (
|
|
13
|
+
<SequencesScreen
|
|
14
|
+
onOpenContact={(id) => router.push(`/contacts?id=${id}`)}
|
|
15
|
+
renderDetailActions={(sequence) => <SequenceCampaignLink sequence={sequence} onOpenCampaign={(id) => router.push(`/campaigns?id=${encodeURIComponent(id)}`)} />}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
12
18
|
}
|
package/stacks/outreach.json
CHANGED
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"dialer",
|
|
17
17
|
"phone-numbers",
|
|
18
18
|
"appointments",
|
|
19
|
-
"analytics"
|
|
19
|
+
"analytics",
|
|
20
|
+
"email-signatures"
|
|
20
21
|
],
|
|
21
22
|
"home": "/analytics",
|
|
22
23
|
"nav": [
|
|
@@ -76,7 +77,8 @@
|
|
|
76
77
|
"component": "CampaignsScreen",
|
|
77
78
|
"icon": "Rocket",
|
|
78
79
|
"group": "Engage",
|
|
79
|
-
"description": "Campaigns from brief to launch"
|
|
80
|
+
"description": "Campaigns from brief to launch",
|
|
81
|
+
"page": "pages/campaigns.tsx.tmpl"
|
|
80
82
|
},
|
|
81
83
|
{
|
|
82
84
|
"label": "Inbox",
|
|
@@ -145,6 +147,15 @@
|
|
|
145
147
|
"group": "Setup",
|
|
146
148
|
"description": "Sending mailboxes and warmup health"
|
|
147
149
|
},
|
|
150
|
+
{
|
|
151
|
+
"label": "Email signatures",
|
|
152
|
+
"path": "/email-signatures",
|
|
153
|
+
"block": "email-signatures",
|
|
154
|
+
"component": "EmailSignaturesScreen",
|
|
155
|
+
"icon": "PenLine",
|
|
156
|
+
"group": "Setup",
|
|
157
|
+
"description": "One signature for the team, kept on every mailbox"
|
|
158
|
+
},
|
|
148
159
|
{
|
|
149
160
|
"label": "Phone numbers",
|
|
150
161
|
"path": "/phone-numbers",
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { RecordMeetingsTab } from "@/components/g8/appointments/record-meetings-tab";
|
|
6
6
|
import { ContactsScreen } from "@/components/g8/crm/contacts-screen";
|
|
7
7
|
import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
|
|
8
|
+
import { CreateDealButton } from "@/components/g8/deals/create-deal-button";
|
|
8
9
|
import { RecordDealsTab } from "@/components/g8/deals/record-deals-tab";
|
|
9
10
|
import { RecordConversationsTab } from "@/components/g8/inbox/record-conversations-tab";
|
|
10
11
|
import { RecordQuotesTab } from "@/components/g8/quotes/record-quotes-tab";
|
|
@@ -15,6 +16,7 @@ export default function Page() {
|
|
|
15
16
|
return (
|
|
16
17
|
<ContactsScreen
|
|
17
18
|
onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
|
|
19
|
+
renderDetailActions={(c) => <CreateDealButton contactId={c.id} onCreated={(id) => router.push(`/deals?id=${encodeURIComponent(id)}`)} />}
|
|
18
20
|
extraDetailTabs={[
|
|
19
21
|
{ id: "meetings", label: "Meetings", render: (c) => <RecordMeetingsTab contactId={c.id} onOpenMeeting={(sel) => router.push(`/meetings?sel=${encodeURIComponent(sel)}`)} /> },
|
|
20
22
|
{ id: "quotes", label: "Quotes", render: (c) => <RecordQuotesTab kind="contacts" id={c.id} onOpenQuote={(id) => router.push(`/quotes?id=${encodeURIComponent(id)}`)} /> },
|
|
@@ -10,7 +10,7 @@ import { ApiError } from "@/components/g8/_foundation/api-error";
|
|
|
10
10
|
import { EmptyState } from "@/components/g8/_foundation/empty-state";
|
|
11
11
|
import { useG8Query } from "@/components/g8/_foundation/hooks/use-g8-query";
|
|
12
12
|
import { g8Get } from "@/components/g8/_foundation/lib/g8-client";
|
|
13
|
-
import { ContactLookupLink } from "@/components/g8/crm/contact-lookup-link";
|
|
13
|
+
import { ContactLookupLink, splitName } from "@/components/g8/crm/contact-lookup-link";
|
|
14
14
|
import { DealDetail } from "@/components/g8/deals/deal-detail";
|
|
15
15
|
import { useDefaultOwner } from "@/components/g8/deals/hooks/use-default-owner";
|
|
16
16
|
import { InboxScreen } from "@/components/g8/inbox/inbox-screen";
|
|
@@ -94,7 +94,7 @@ export default function Page() {
|
|
|
94
94
|
<InboxScreen
|
|
95
95
|
renderDetailActions={(thread) => (
|
|
96
96
|
<>
|
|
97
|
-
<ContactLookupLink email={thread.contact?.email} onOpen={(id) => router.push(`/contacts?id=${id}`)} />
|
|
97
|
+
<ContactLookupLink email={thread.contact?.email} createIfMissing={splitName(thread.contact?.name)} onOpen={(id) => router.push(`/contacts?id=${id}`)} />
|
|
98
98
|
<CreateDeal thread={thread} />
|
|
99
99
|
</>
|
|
100
100
|
)}
|
package/stacks/pipedrive.json
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"foundation",
|
|
9
9
|
"crm",
|
|
10
10
|
"mailboxes",
|
|
11
|
+
"email-signatures",
|
|
11
12
|
"inbox",
|
|
12
13
|
"appointments",
|
|
13
14
|
"deals",
|
|
@@ -112,6 +113,15 @@
|
|
|
112
113
|
"icon": "Mail",
|
|
113
114
|
"group": "Setup",
|
|
114
115
|
"description": "Sending mailboxes and warmup health"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"label": "Email signatures",
|
|
119
|
+
"path": "/email-signatures",
|
|
120
|
+
"block": "email-signatures",
|
|
121
|
+
"component": "EmailSignaturesScreen",
|
|
122
|
+
"icon": "PenLine",
|
|
123
|
+
"group": "Setup",
|
|
124
|
+
"description": "One signature for the team, kept on every mailbox"
|
|
115
125
|
}
|
|
116
126
|
]
|
|
117
127
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { CompaniesScreen } from "@/components/g8/crm/companies-screen";
|
|
6
6
|
import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
|
|
7
7
|
import { RecordDealsTab } from "@/components/g8/deals/record-deals-tab";
|
|
8
|
+
import { RecordDocumentsTab } from "@/components/g8/documents/record-documents-tab";
|
|
8
9
|
import { AddCompanyPeopleToListButton } from "@/components/g8/lists/add-company-people-to-list-button";
|
|
9
10
|
import { RecordQuotesTab } from "@/components/g8/quotes/record-quotes-tab";
|
|
10
11
|
import { useRouter } from "next/navigation";
|
|
@@ -19,6 +20,7 @@ export default function Page() {
|
|
|
19
20
|
extraDetailTabs={[
|
|
20
21
|
{ id: "quotes", label: "Quotes", render: (c) => <RecordQuotesTab kind="companies" id={c.id} onOpenQuote={(id) => router.push(`/quotes?id=${encodeURIComponent(id)}`)} /> },
|
|
21
22
|
{ id: "deals", label: "Deals", render: (c) => <RecordDealsTab kind="companies" id={c.id} onOpenDeal={(id) => router.push(`/deals?id=${encodeURIComponent(id)}`)} /> },
|
|
23
|
+
{ id: "documents", label: "Documents", render: (c) => <RecordDocumentsTab companyId={c.id} onOpenDocuments={(href) => router.push(href)} /> },
|
|
22
24
|
{ id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="companies" id={c.id} /> },
|
|
23
25
|
]}
|
|
24
26
|
/>
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
// Generated by `graph8-sdk create {{stackId}}`. The {{block}} block's {{component}} (source in {{blockDir}}), with the
|
|
4
4
|
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
|
+
import { MemberPicker } from "@/components/g8/pickers/member-picker";
|
|
5
6
|
import { ContactsScreen } from "@/components/g8/crm/contacts-screen";
|
|
6
7
|
import { RecordEngagementTab } from "@/components/g8/crm/record-engagement-tab";
|
|
8
|
+
import { CreateDealButton } from "@/components/g8/deals/create-deal-button";
|
|
7
9
|
import { RecordDealsTab } from "@/components/g8/deals/record-deals-tab";
|
|
10
|
+
import { RecordDocumentsTab } from "@/components/g8/documents/record-documents-tab";
|
|
8
11
|
import { AddToListButton } from "@/components/g8/lists/add-to-list-button";
|
|
9
12
|
import { RecordListsTab } from "@/components/g8/lists/record-lists-tab";
|
|
10
13
|
import { RecordQuotesTab } from "@/components/g8/quotes/record-quotes-tab";
|
|
@@ -14,17 +17,20 @@ export default function Page() {
|
|
|
14
17
|
const router = useRouter();
|
|
15
18
|
return (
|
|
16
19
|
<ContactsScreen
|
|
20
|
+
renderOwnerPicker={(picker) => <MemberPicker label="New owner" {...picker} />}
|
|
17
21
|
onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
|
|
18
22
|
renderBulkActions={({ ids, clear }) => <AddToListButton contactIds={ids} onAdded={clear} />}
|
|
19
23
|
renderDetailActions={(c) => (
|
|
20
24
|
<>
|
|
21
25
|
<AddToListButton contactIds={[c.id]} />
|
|
26
|
+
<CreateDealButton contactId={c.id} onCreated={(id) => router.push(`/deals?id=${encodeURIComponent(id)}`)} />
|
|
22
27
|
</>
|
|
23
28
|
)}
|
|
24
29
|
extraDetailTabs={[
|
|
25
30
|
{ id: "lists", label: "Lists", render: (c) => <RecordListsTab contactId={c.id} onOpenList={(id) => router.push(`/lists?id=${id}`)} /> },
|
|
26
31
|
{ id: "quotes", label: "Quotes", render: (c) => <RecordQuotesTab kind="contacts" id={c.id} onOpenQuote={(id) => router.push(`/quotes?id=${encodeURIComponent(id)}`)} /> },
|
|
27
32
|
{ id: "deals", label: "Deals", render: (c) => <RecordDealsTab kind="contacts" id={c.id} onOpenDeal={(id) => router.push(`/deals?id=${encodeURIComponent(id)}`)} /> },
|
|
33
|
+
{ id: "documents", label: "Documents", render: (c) => <RecordDocumentsTab contactId={c.id} onOpenDocuments={(href) => router.push(href)} /> },
|
|
28
34
|
{ id: "engagement", label: "Engagement", render: (c) => <RecordEngagementTab kind="contacts" id={c.id} /> },
|
|
29
35
|
]}
|
|
30
36
|
/>
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// other blocks of this app plugged into its slots. Edit freely: this file is yours.
|
|
5
5
|
import { useRouter } from "next/navigation";
|
|
6
6
|
import { DealsScreen } from "@/components/g8/deals/deals-screen";
|
|
7
|
+
import { RecordDocumentsTab } from "@/components/g8/documents/record-documents-tab";
|
|
7
8
|
import { CreateQuoteFromDeal } from "@/components/g8/quotes/create-quote-from-deal";
|
|
8
9
|
|
|
9
10
|
/** A deal opens its quotes, its contacts and (from a contact) their company. */
|
|
@@ -15,6 +16,9 @@ export default function Page() {
|
|
|
15
16
|
renderQuoteActions={(deal) => <CreateQuoteFromDeal dealId={deal.id} />}
|
|
16
17
|
onOpenContact={(id) => router.push(`/contacts?id=${id}`)}
|
|
17
18
|
onOpenCompany={(id) => router.push(`/companies?id=${id}`)}
|
|
19
|
+
extraDetailTabs={[
|
|
20
|
+
{ id: "documents", label: "Documents", render: (d) => <RecordDocumentsTab contactId={d.primary_contact?.id} scope="account" onOpenDocuments={(href) => router.push(href)} /> },
|
|
21
|
+
]}
|
|
18
22
|
/>
|
|
19
23
|
);
|
|
20
24
|
}
|
package/stacks/salesforce.json
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"lifecycle",
|
|
14
14
|
"deals",
|
|
15
15
|
"quotes",
|
|
16
|
+
"documents",
|
|
16
17
|
"analytics",
|
|
17
18
|
"workflows",
|
|
18
19
|
"copilot",
|
|
@@ -99,6 +100,15 @@
|
|
|
99
100
|
"description": "Follow-ups due across your records",
|
|
100
101
|
"page": "pages/tasks.tsx.tmpl"
|
|
101
102
|
},
|
|
103
|
+
{
|
|
104
|
+
"label": "Documents",
|
|
105
|
+
"path": "/documents",
|
|
106
|
+
"block": "documents",
|
|
107
|
+
"component": "DocumentsScreen",
|
|
108
|
+
"icon": "FileText",
|
|
109
|
+
"group": "Work",
|
|
110
|
+
"description": "Library, security questionnaires and account correspondence"
|
|
111
|
+
},
|
|
102
112
|
{
|
|
103
113
|
"label": "Workflows",
|
|
104
114
|
"path": "/workflows",
|